mlrun 1.7.0rc42__py3-none-any.whl → 1.7.0rc45__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 (54) hide show
  1. mlrun/__main__.py +4 -2
  2. mlrun/artifacts/manager.py +9 -2
  3. mlrun/common/schemas/__init__.py +1 -0
  4. mlrun/common/schemas/alert.py +11 -11
  5. mlrun/common/schemas/auth.py +2 -0
  6. mlrun/common/schemas/client_spec.py +0 -1
  7. mlrun/common/schemas/frontend_spec.py +7 -0
  8. mlrun/common/schemas/notification.py +32 -5
  9. mlrun/common/schemas/workflow.py +1 -0
  10. mlrun/config.py +47 -22
  11. mlrun/data_types/data_types.py +5 -0
  12. mlrun/datastore/base.py +4 -7
  13. mlrun/datastore/storeytargets.py +4 -3
  14. mlrun/datastore/targets.py +17 -4
  15. mlrun/db/httpdb.py +10 -12
  16. mlrun/db/nopdb.py +21 -4
  17. mlrun/execution.py +7 -2
  18. mlrun/feature_store/api.py +1 -0
  19. mlrun/feature_store/retrieval/spark_merger.py +7 -3
  20. mlrun/frameworks/_common/plan.py +3 -3
  21. mlrun/frameworks/_ml_common/plan.py +1 -1
  22. mlrun/frameworks/parallel_coordinates.py +2 -3
  23. mlrun/k8s_utils.py +48 -2
  24. mlrun/launcher/client.py +6 -6
  25. mlrun/model.py +2 -1
  26. mlrun/model_monitoring/applications/results.py +2 -2
  27. mlrun/model_monitoring/controller.py +1 -1
  28. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +15 -1
  29. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +12 -0
  30. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +2 -2
  31. mlrun/model_monitoring/helpers.py +7 -15
  32. mlrun/model_monitoring/writer.py +8 -2
  33. mlrun/projects/pipelines.py +2 -0
  34. mlrun/projects/project.py +152 -60
  35. mlrun/render.py +3 -3
  36. mlrun/runtimes/daskjob.py +1 -1
  37. mlrun/runtimes/kubejob.py +6 -6
  38. mlrun/runtimes/local.py +4 -1
  39. mlrun/runtimes/nuclio/api_gateway.py +6 -0
  40. mlrun/runtimes/nuclio/application/application.py +5 -4
  41. mlrun/runtimes/nuclio/function.py +45 -0
  42. mlrun/runtimes/pod.py +21 -13
  43. mlrun/runtimes/sparkjob/spark3job.py +4 -0
  44. mlrun/serving/server.py +2 -0
  45. mlrun/utils/async_http.py +1 -1
  46. mlrun/utils/helpers.py +39 -16
  47. mlrun/utils/notifications/notification/__init__.py +0 -1
  48. mlrun/utils/version/version.json +2 -2
  49. {mlrun-1.7.0rc42.dist-info → mlrun-1.7.0rc45.dist-info}/METADATA +27 -27
  50. {mlrun-1.7.0rc42.dist-info → mlrun-1.7.0rc45.dist-info}/RECORD +54 -54
  51. {mlrun-1.7.0rc42.dist-info → mlrun-1.7.0rc45.dist-info}/WHEEL +1 -1
  52. {mlrun-1.7.0rc42.dist-info → mlrun-1.7.0rc45.dist-info}/LICENSE +0 -0
  53. {mlrun-1.7.0rc42.dist-info → mlrun-1.7.0rc45.dist-info}/entry_points.txt +0 -0
  54. {mlrun-1.7.0rc42.dist-info → mlrun-1.7.0rc45.dist-info}/top_level.txt +0 -0
mlrun/projects/project.py CHANGED
@@ -18,6 +18,7 @@ import glob
18
18
  import http
19
19
  import importlib.util as imputil
20
20
  import json
21
+ import os
21
22
  import pathlib
22
23
  import shutil
23
24
  import tempfile
@@ -25,6 +26,7 @@ import typing
25
26
  import uuid
26
27
  import warnings
27
28
  import zipfile
29
+ from copy import deepcopy
28
30
  from os import environ, makedirs, path
29
31
  from typing import Callable, Optional, Union
30
32
 
@@ -65,13 +67,7 @@ from ..features import Feature
65
67
  from ..model import EntrypointParam, ImageBuilder, ModelObj
66
68
  from ..run import code_to_function, get_object, import_function, new_function
67
69
  from ..secrets import SecretsStore
68
- from ..utils import (
69
- is_ipython,
70
- is_relative_path,
71
- is_yaml_path,
72
- logger,
73
- update_in,
74
- )
70
+ from ..utils import is_jupyter, is_relative_path, is_yaml_path, logger, update_in
75
71
  from ..utils.clones import (
76
72
  add_credentials_git_remote_url,
77
73
  clone_git,
@@ -251,8 +247,7 @@ def new_project(
251
247
  project.spec.description = description
252
248
 
253
249
  if default_function_node_selector:
254
- for key, val in default_function_node_selector.items():
255
- project.spec.default_function_node_selector[key] = val
250
+ project.spec.default_function_node_selector = default_function_node_selector
256
251
 
257
252
  if parameters:
258
253
  # Enable setting project parameters at load time, can be used to customize the project_setup
@@ -874,7 +869,7 @@ class ProjectSpec(ModelObj):
874
869
  # in a tuple where the first index is the packager module's path (str) and the second is a flag (bool) for
875
870
  # whether it is mandatory for a run (raise exception on collection error) or not.
876
871
  self.custom_packagers = custom_packagers or []
877
- self.default_function_node_selector = default_function_node_selector or {}
872
+ self._default_function_node_selector = default_function_node_selector or None
878
873
 
879
874
  @property
880
875
  def source(self) -> str:
@@ -1049,6 +1044,14 @@ class ProjectSpec(ModelObj):
1049
1044
  if key in self._artifacts:
1050
1045
  del self._artifacts[key]
1051
1046
 
1047
+ @property
1048
+ def default_function_node_selector(self):
1049
+ return self._default_function_node_selector
1050
+
1051
+ @default_function_node_selector.setter
1052
+ def default_function_node_selector(self, node_selector: dict[str, str]):
1053
+ self._default_function_node_selector = deepcopy(node_selector)
1054
+
1052
1055
  @property
1053
1056
  def build(self) -> ImageBuilder:
1054
1057
  return self._build
@@ -1590,7 +1593,9 @@ class MlrunProject(ModelObj):
1590
1593
  :param format: artifact file format: csv, png, ..
1591
1594
  :param tag: version tag
1592
1595
  :param target_path: absolute target path (instead of using artifact_path + local_path)
1593
- :param upload: upload to datastore (default is True)
1596
+ :param upload: Whether to upload the artifact to the datastore. If not provided, and the `local_path`
1597
+ is not a directory, upload occurs by default. Directories are uploaded only when this
1598
+ flag is explicitly set to `True`.
1594
1599
  :param labels: a set of key/value labels to tag the artifact with
1595
1600
 
1596
1601
  :returns: artifact object
@@ -2325,31 +2330,51 @@ class MlrunProject(ModelObj):
2325
2330
  requirements: typing.Union[str, list[str]] = None,
2326
2331
  requirements_file: str = "",
2327
2332
  ) -> mlrun.runtimes.BaseRuntime:
2328
- """update or add a function object to the project
2333
+ """
2334
+ | Update or add a function object to the project.
2335
+ | Function can be provided as an object (func) or a .py/.ipynb/.yaml URL.
2329
2336
 
2330
- function can be provided as an object (func) or a .py/.ipynb/.yaml url
2331
- support url prefixes::
2337
+ | Creating a function from a single file is done by specifying ``func`` and disabling ``with_repo``.
2338
+ | Creating a function with project source (specify ``with_repo=True``):
2339
+ | 1. Specify a relative ``func`` path.
2340
+ | 2. Specify a module ``handler`` (e.g. ``handler=package.package.func``) without ``func``.
2341
+ | Creating a function with non project source is done by specifying a module ``handler`` and on the
2342
+ returned function set the source with ``function.with_source_archive(<source>)``.
2332
2343
 
2333
- object (s3://, v3io://, ..)
2334
- MLRun DB e.g. db://project/func:ver
2335
- functions hub/market: e.g. hub://auto-trainer:master
2344
+ Support URL prefixes:
2336
2345
 
2337
- examples::
2346
+ | Object (s3://, v3io://, ..)
2347
+ | MLRun DB e.g. db://project/func:ver
2348
+ | Functions hub/market: e.g. hub://auto-trainer:master
2349
+
2350
+ Examples::
2338
2351
 
2339
2352
  proj.set_function(func_object)
2340
- proj.set_function(
2341
- "./src/mycode.py", "ingest", image="myrepo/ing:latest", with_repo=True
2342
- )
2343
2353
  proj.set_function("http://.../mynb.ipynb", "train")
2344
2354
  proj.set_function("./func.yaml")
2345
2355
  proj.set_function("hub://get_toy_data", "getdata")
2346
2356
 
2347
- # set function requirements
2357
+ # Create a function from a single file
2358
+ proj.set_function("./src/mycode.py", "ingest")
2348
2359
 
2349
- # by providing a list of packages
2360
+ # Creating a function with project source
2361
+ proj.set_function(
2362
+ "./src/mycode.py", "ingest", image="myrepo/ing:latest", with_repo=True
2363
+ )
2364
+ proj.set_function("ingest", handler="package.package.func", with_repo=True)
2365
+
2366
+ # Creating a function with non project source
2367
+ func = proj.set_function(
2368
+ "ingest", handler="package.package.func", with_repo=False
2369
+ )
2370
+ func.with_source_archive("git://github.com/mlrun/something.git")
2371
+
2372
+ # Set function requirements
2373
+
2374
+ # By providing a list of packages
2350
2375
  proj.set_function("my.py", requirements=["requests", "pandas"])
2351
2376
 
2352
- # by providing a path to a pip requirements file
2377
+ # By providing a path to a pip requirements file
2353
2378
  proj.set_function("my.py", requirements="requirements.txt")
2354
2379
 
2355
2380
  :param func: Function object or spec/code url, None refers to current Notebook
@@ -2369,7 +2394,7 @@ class MlrunProject(ModelObj):
2369
2394
  :param requirements: A list of python packages
2370
2395
  :param requirements_file: Path to a python requirements file
2371
2396
 
2372
- :returns: function object
2397
+ :returns: :py:class:`~mlrun.runtimes.BaseRuntime`
2373
2398
  """
2374
2399
  (
2375
2400
  resolved_function_name,
@@ -2410,7 +2435,7 @@ class MlrunProject(ModelObj):
2410
2435
  ):
2411
2436
  # if function path is not provided and it is not a module (no ".")
2412
2437
  # use the current notebook as default
2413
- if is_ipython:
2438
+ if is_jupyter:
2414
2439
  from IPython import get_ipython
2415
2440
 
2416
2441
  kernel = get_ipython()
@@ -2801,47 +2826,94 @@ class MlrunProject(ModelObj):
2801
2826
  secrets=secrets or {},
2802
2827
  )
2803
2828
 
2804
- def sync_functions(self, names: list = None, always=True, save=False):
2805
- """reload function objects from specs and files"""
2829
+ def sync_functions(
2830
+ self,
2831
+ names: list = None,
2832
+ always: bool = True,
2833
+ save: bool = False,
2834
+ silent: bool = False,
2835
+ ):
2836
+ """
2837
+ Reload function objects from specs and files.
2838
+ The function objects are synced against the definitions spec in `self.spec._function_definitions`.
2839
+ Referenced files/URLs in the function spec will be reloaded.
2840
+ Function definitions are parsed by the following precedence:
2841
+
2842
+ 1. Contains runtime spec.
2843
+ 2. Contains module in the project's context.
2844
+ 3. Contains path to function definition (yaml, DB, Hub).
2845
+ 4. Contains path to .ipynb or .py files.
2846
+ 5. Contains a Nuclio/Serving function image / an 'Application' kind definition.
2847
+
2848
+ If function definition is already an object, some project metadata updates will apply however,
2849
+ it will not be reloaded.
2850
+
2851
+ :param names: Names of functions to reload, defaults to `self.spec._function_definitions.keys()`.
2852
+ :param always: Force reloading the functions.
2853
+ :param save: Whether to save the loaded functions or not.
2854
+ :param silent: Whether to raise an exception when a function fails to load.
2855
+
2856
+ :returns: Dictionary of function objects
2857
+ """
2806
2858
  if self._initialized and not always:
2807
2859
  return self.spec._function_objects
2808
2860
 
2809
- funcs = self.spec._function_objects
2861
+ functions = self.spec._function_objects
2810
2862
  if not names:
2811
2863
  names = self.spec._function_definitions.keys()
2812
- funcs = {}
2864
+ functions = {}
2865
+
2813
2866
  origin = mlrun.runtimes.utils.add_code_metadata(self.spec.context)
2814
2867
  for name in names:
2815
- f = self.spec._function_definitions.get(name)
2816
- if not f:
2817
- raise ValueError(f"function named {name} not found")
2868
+ function_definition = self.spec._function_definitions.get(name)
2869
+ if not function_definition:
2870
+ if silent:
2871
+ logger.warn(
2872
+ "Function definition was not found, skipping reload", name=name
2873
+ )
2874
+ continue
2875
+
2876
+ raise ValueError(f"Function named {name} not found")
2877
+
2878
+ function_object = self.spec._function_objects.get(name, None)
2879
+ is_base_runtime = isinstance(
2880
+ function_object, mlrun.runtimes.base.BaseRuntime
2881
+ )
2818
2882
  # If this function is already available locally, don't recreate it unless always=True
2819
- if (
2820
- isinstance(
2821
- self.spec._function_objects.get(name, None),
2822
- mlrun.runtimes.base.BaseRuntime,
2823
- )
2824
- and not always
2825
- ):
2826
- funcs[name] = self.spec._function_objects[name]
2883
+ if is_base_runtime and not always:
2884
+ functions[name] = function_object
2827
2885
  continue
2828
- if hasattr(f, "to_dict"):
2829
- name, func = _init_function_from_obj(f, self, name)
2830
- else:
2831
- if not isinstance(f, dict):
2832
- raise ValueError("function must be an object or dict")
2886
+
2887
+ # Reload the function
2888
+ if hasattr(function_definition, "to_dict"):
2889
+ name, func = _init_function_from_obj(function_definition, self, name)
2890
+ elif isinstance(function_definition, dict):
2833
2891
  try:
2834
- name, func = _init_function_from_dict(f, self, name)
2892
+ name, func = _init_function_from_dict(
2893
+ function_definition, self, name
2894
+ )
2835
2895
  except FileNotFoundError as exc:
2836
- raise mlrun.errors.MLRunMissingDependencyError(
2837
- f"File {exc.filename} not found while syncing project functions"
2838
- ) from exc
2896
+ message = f"File {exc.filename} not found while syncing project functions."
2897
+ if silent:
2898
+ message += " Skipping function reload"
2899
+ logger.warn(message, name=name)
2900
+ continue
2901
+
2902
+ raise mlrun.errors.MLRunMissingDependencyError(message) from exc
2903
+ else:
2904
+ message = f"Function {name} must be an object or dict."
2905
+ if silent:
2906
+ message += " Skipping function reload"
2907
+ logger.warn(message, name=name)
2908
+ continue
2909
+ raise ValueError(message)
2910
+
2839
2911
  func.spec.build.code_origin = origin
2840
- funcs[name] = func
2912
+ functions[name] = func
2841
2913
  if save:
2842
2914
  func.save(versioned=False)
2843
2915
 
2844
- self.spec._function_objects = funcs
2916
+ self.spec._function_objects = functions
2845
2917
  self._initialized = True
2846
2918
  return self.spec._function_objects
2847
2919
 
@@ -2986,6 +3058,7 @@ class MlrunProject(ModelObj):
2986
3058
  source: str = None,
2987
3059
  cleanup_ttl: int = None,
2988
3060
  notifications: list[mlrun.model.Notification] = None,
3061
+ workflow_runner_node_selector: typing.Optional[dict[str, str]] = None,
2989
3062
  ) -> _PipelineRunStatus:
2990
3063
  """Run a workflow using kubeflow pipelines
2991
3064
 
@@ -3014,15 +3087,20 @@ class MlrunProject(ModelObj):
3014
3087
 
3015
3088
  * Remote URL which is loaded dynamically to the workflow runner.
3016
3089
  * A path to the project's context on the workflow runner's image.
3017
- Path can be absolute or relative to `project.spec.build.source_code_target_dir` if defined
3018
- (enriched when building a project image with source, see `MlrunProject.build_image`).
3019
- For other engines the source is used to validate that the code is up-to-date.
3090
+ Path can be absolute or relative to `project.spec.build.source_code_target_dir` if defined
3091
+ (enriched when building a project image with source, see `MlrunProject.build_image`).
3092
+ For other engines the source is used to validate that the code is up-to-date.
3093
+
3020
3094
  :param cleanup_ttl:
3021
3095
  Pipeline cleanup ttl in secs (time to wait after workflow completion, at which point the
3022
3096
  workflow and all its resources are deleted)
3023
3097
  :param notifications:
3024
3098
  List of notifications to send for workflow completion
3025
-
3099
+ :param workflow_runner_node_selector:
3100
+ Defines the node selector for the workflow runner pod when using a remote engine.
3101
+ This allows you to control and specify where the workflow runner pod will be scheduled.
3102
+ This setting is only relevant when the engine is set to 'remote' or for scheduled workflows,
3103
+ and it will be ignored if the workflow is not run on a remote engine.
3026
3104
  :returns: ~py:class:`~mlrun.projects.pipelines._PipelineRunStatus` instance
3027
3105
  """
3028
3106
 
@@ -3041,11 +3119,10 @@ class MlrunProject(ModelObj):
3041
3119
  )
3042
3120
 
3043
3121
  if engine not in ["remote"] and not schedule:
3044
- # For remote/scheduled runs we don't require the functions to be synced as they can be loaded dynamically
3045
- # during run
3046
- self.sync_functions(always=sync)
3122
+ # For remote/scheduled runs there is no need to sync functions as they can be loaded dynamically during run
3123
+ self.sync_functions(always=sync, silent=True)
3047
3124
  if not self.spec._function_objects:
3048
- raise ValueError(
3125
+ logger.warn(
3049
3126
  "There are no functions in the project."
3050
3127
  " Make sure you've set your functions with project.set_function()."
3051
3128
  )
@@ -3089,6 +3166,16 @@ class MlrunProject(ModelObj):
3089
3166
  )
3090
3167
  inner_engine = get_workflow_engine(engine_kind, local).engine
3091
3168
  workflow_spec.engine = inner_engine or workflow_engine.engine
3169
+ if workflow_runner_node_selector:
3170
+ if workflow_engine.engine == "remote":
3171
+ workflow_spec.workflow_runner_node_selector = (
3172
+ workflow_runner_node_selector
3173
+ )
3174
+ else:
3175
+ logger.warn(
3176
+ "'workflow_runner_node_selector' applies only to remote engines"
3177
+ " and is ignored for non-remote runs."
3178
+ )
3092
3179
 
3093
3180
  run = workflow_engine.run(
3094
3181
  self,
@@ -4289,6 +4376,7 @@ class MlrunProject(ModelObj):
4289
4376
  kind=producer_dict.get("kind", ""),
4290
4377
  project=producer_project,
4291
4378
  tag=producer_tag,
4379
+ owner=producer_dict.get("owner", ""),
4292
4380
  ), True
4293
4381
 
4294
4382
  # do not retain the artifact's producer, replace it with the project as the producer
@@ -4298,6 +4386,7 @@ class MlrunProject(ModelObj):
4298
4386
  name=self.metadata.name,
4299
4387
  project=self.metadata.name,
4300
4388
  tag=project_producer_tag,
4389
+ owner=self._resolve_artifact_owner(),
4301
4390
  ), False
4302
4391
 
4303
4392
  def _resolve_existing_artifact(
@@ -4337,6 +4426,9 @@ class MlrunProject(ModelObj):
4337
4426
  def _get_project_tag(self):
4338
4427
  return self._get_hexsha() or str(uuid.uuid4())
4339
4428
 
4429
+ def _resolve_artifact_owner(self):
4430
+ return os.getenv("V3IO_USERNAME") or self.spec.owner
4431
+
4340
4432
 
4341
4433
  def _set_as_current_default_project(project: MlrunProject):
4342
4434
  mlrun.mlconf.default_project = project.metadata.name
mlrun/render.py CHANGED
@@ -22,7 +22,7 @@ import mlrun.utils
22
22
 
23
23
  from .config import config
24
24
  from .datastore import uri_to_ipython
25
- from .utils import dict_to_list, get_in, is_ipython
25
+ from .utils import dict_to_list, get_in, is_jupyter
26
26
 
27
27
  JUPYTER_SERVER_ROOT = environ.get("HOME", "/User")
28
28
  supported_viewers = [
@@ -181,8 +181,8 @@ def run_to_html(results, display=True):
181
181
 
182
182
 
183
183
  def ipython_display(html, display=True, alt_text=None):
184
- if display and html and is_ipython:
185
- import IPython
184
+ if display and html and is_jupyter:
185
+ import IPython.display
186
186
 
187
187
  IPython.display.display(IPython.display.HTML(html))
188
188
  elif alt_text:
mlrun/runtimes/daskjob.py CHANGED
@@ -379,7 +379,7 @@ class DaskCluster(KubejobRuntime):
379
379
  :param show_on_failure: show logs only in case of build failure
380
380
  :param force_build: force building the image, even when no changes were made
381
381
 
382
- :return True if the function is ready (deployed)
382
+ :return: True if the function is ready (deployed)
383
383
  """
384
384
  return super().deploy(
385
385
  watch,
mlrun/runtimes/kubejob.py CHANGED
@@ -11,7 +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
-
14
+ import typing
15
15
  import warnings
16
16
 
17
17
  from mlrun_pipelines.common.ops import build_op
@@ -143,11 +143,11 @@ class KubejobRuntime(KubeResource):
143
143
 
144
144
  def deploy(
145
145
  self,
146
- watch=True,
147
- with_mlrun=None,
148
- skip_deployed=False,
149
- is_kfp=False,
150
- mlrun_version_specifier=None,
146
+ watch: bool = True,
147
+ with_mlrun: typing.Optional[bool] = None,
148
+ skip_deployed: bool = False,
149
+ is_kfp: bool = False,
150
+ mlrun_version_specifier: typing.Optional[bool] = None,
151
151
  builder_env: dict = None,
152
152
  show_on_failure: bool = False,
153
153
  force_build: bool = False,
mlrun/runtimes/local.py CHANGED
@@ -145,7 +145,10 @@ class ParallelRunner:
145
145
  if function_name and generator.options.teardown_dask:
146
146
  logger.info("Tearing down the dask cluster..")
147
147
  mlrun.get_run_db().delete_runtime_resources(
148
- kind="dask", object_id=function_name, force=True
148
+ project=self.metadata.project,
149
+ kind=mlrun.runtimes.RuntimeKinds.dask,
150
+ object_id=function_name,
151
+ force=True,
149
152
  )
150
153
 
151
154
  return results
@@ -587,6 +587,12 @@ class APIGateway(ModelObj):
587
587
  self.metadata.annotations, gateway_timeout
588
588
  )
589
589
 
590
+ def with_annotations(self, annotations: dict):
591
+ """set a key/value annotations in the metadata of the api gateway"""
592
+ for key, value in annotations.items():
593
+ self.metadata.annotations[key] = str(value)
594
+ return self
595
+
590
596
  @classmethod
591
597
  def from_scheme(cls, api_gateway: schemas.APIGateway):
592
598
  project = api_gateway.metadata.labels.get(
@@ -438,8 +438,10 @@ class ApplicationRuntime(RemoteRuntime):
438
438
  """
439
439
  Create the application API gateway. Once the application is deployed, the API gateway can be created.
440
440
  An application without an API gateway is not accessible.
441
- :param name: The name of the API gateway, defaults to <function-name>-<function-tag>
442
- :param path: Optional path of the API gateway, default value is "/"
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
443
445
  :param direct_port_access: Set True to allow direct port access to the application sidecar
444
446
  :param authentication_mode: API Gateway authentication mode
445
447
  :param authentication_creds: API Gateway basic authentication credentials as a tuple (username, password)
@@ -448,8 +450,7 @@ class ApplicationRuntime(RemoteRuntime):
448
450
  :param set_as_default: Set the API gateway as the default for the application (`status.api_gateway`)
449
451
  :param gateway_timeout: nginx ingress timeout in sec (request timeout, when will the gateway return an
450
452
  error)
451
-
452
- :return: The API gateway URL
453
+ :return: The API gateway URL
453
454
  """
454
455
  if not name:
455
456
  raise mlrun.errors.MLRunInvalidArgumentError(
@@ -23,6 +23,7 @@ import inflection
23
23
  import nuclio
24
24
  import nuclio.utils
25
25
  import requests
26
+ import semver
26
27
  from aiohttp.client import ClientSession
27
28
  from kubernetes import client
28
29
  from mlrun_pipelines.common.mounts import VolumeMount
@@ -296,10 +297,37 @@ class RemoteRuntime(KubeResource):
296
297
  """
297
298
  if hasattr(spec, "to_dict"):
298
299
  spec = spec.to_dict()
300
+
301
+ self._validate_triggers(spec)
302
+
299
303
  spec["name"] = name
300
304
  self.spec.config[f"spec.triggers.{name}"] = spec
301
305
  return self
302
306
 
307
+ def _validate_triggers(self, spec):
308
+ # ML-7763 / NUC-233
309
+ min_nuclio_version = "1.13.12"
310
+ if mlconf.nuclio_version and semver.VersionInfo.parse(
311
+ mlconf.nuclio_version
312
+ ) < semver.VersionInfo.parse(min_nuclio_version):
313
+ explicit_ack_enabled = False
314
+ num_triggers = 0
315
+ trigger_name = spec.get("name", "UNKNOWN")
316
+ for key, config in [(f"spec.triggers.{trigger_name}", spec)] + list(
317
+ self.spec.config.items()
318
+ ):
319
+ if key.startswith("spec.triggers."):
320
+ num_triggers += 1
321
+ explicit_ack_enabled = (
322
+ config.get("explicitAckMode", "disable") != "disable"
323
+ )
324
+
325
+ if num_triggers > 1 and explicit_ack_enabled:
326
+ raise mlrun.errors.MLRunInvalidArgumentError(
327
+ "Multiple triggers cannot be used in conjunction with explicit ack. "
328
+ f"Please upgrade to nuclio {min_nuclio_version} or newer."
329
+ )
330
+
303
331
  def with_source_archive(
304
332
  self,
305
333
  source,
@@ -495,6 +523,15 @@ class RemoteRuntime(KubeResource):
495
523
  extra_attributes = extra_attributes or {}
496
524
  if ack_window_size:
497
525
  extra_attributes["ackWindowSize"] = ack_window_size
526
+
527
+ access_key = kwargs.pop("access_key", None)
528
+ if access_key:
529
+ logger.warning(
530
+ "The access_key parameter is deprecated and will be ignored, "
531
+ "use the V3IO_ACCESS_KEY environment variable instead"
532
+ )
533
+ access_key = self._resolve_v3io_access_key()
534
+
498
535
  self.add_trigger(
499
536
  name,
500
537
  V3IOStreamTrigger(
@@ -506,6 +543,7 @@ class RemoteRuntime(KubeResource):
506
543
  webapi=endpoint or "http://v3io-webapi:8081",
507
544
  extra_attributes=extra_attributes,
508
545
  read_batch_size=256,
546
+ access_key=access_key,
509
547
  **kwargs,
510
548
  ),
511
549
  )
@@ -1241,6 +1279,13 @@ class RemoteRuntime(KubeResource):
1241
1279
 
1242
1280
  return self._resolve_invocation_url("", force_external_address)
1243
1281
 
1282
+ @staticmethod
1283
+ def _resolve_v3io_access_key():
1284
+ # Nuclio supports generating access key for v3io stream trigger only from version 1.13.11
1285
+ if validate_nuclio_version_compatibility("1.13.11"):
1286
+ return mlrun.model.Credentials.generate_access_key
1287
+ return None
1288
+
1244
1289
 
1245
1290
  def parse_logs(logs):
1246
1291
  logs = json.loads(logs)
mlrun/runtimes/pod.py CHANGED
@@ -38,6 +38,7 @@ from ..k8s_utils import (
38
38
  generate_preemptible_nodes_affinity_terms,
39
39
  generate_preemptible_nodes_anti_affinity_terms,
40
40
  generate_preemptible_tolerations,
41
+ validate_node_selectors,
41
42
  )
42
43
  from ..utils import logger, update_in
43
44
  from .base import BaseRuntime, FunctionSpec, spec_fields
@@ -1106,12 +1107,12 @@ class KubeResource(BaseRuntime, KfpAdapterMixin):
1106
1107
 
1107
1108
  :param state_thresholds: A dictionary of state to threshold. The supported states are:
1108
1109
 
1109
- * pending_scheduled - The pod/crd is scheduled on a node but not yet running
1110
- * pending_not_scheduled - The pod/crd is not yet scheduled on a node
1111
- * executing - The pod/crd started and is running
1112
- * image_pull_backoff - The pod/crd is in image pull backoff
1113
- See mlrun.mlconf.function.spec.state_thresholds for the default thresholds.
1110
+ * pending_scheduled - The pod/crd is scheduled on a node but not yet running
1111
+ * pending_not_scheduled - The pod/crd is not yet scheduled on a node
1112
+ * executing - The pod/crd started and is running
1113
+ * image_pull_backoff - The pod/crd is in image pull backoff
1114
1114
 
1115
+ See :code:`mlrun.mlconf.function.spec.state_thresholds` for the default thresholds.
1115
1116
  :param patch: Whether to merge the given thresholds with the existing thresholds (True, default)
1116
1117
  or override them (False)
1117
1118
  """
@@ -1175,6 +1176,7 @@ class KubeResource(BaseRuntime, KfpAdapterMixin):
1175
1176
  if node_name:
1176
1177
  self.spec.node_name = node_name
1177
1178
  if node_selector is not None:
1179
+ validate_node_selectors(node_selectors=node_selector, raise_on_error=False)
1178
1180
  self.spec.node_selector = node_selector
1179
1181
  if affinity is not None:
1180
1182
  self.spec.affinity = affinity
@@ -1345,20 +1347,26 @@ class KubeResource(BaseRuntime, KfpAdapterMixin):
1345
1347
 
1346
1348
  def _build_image(
1347
1349
  self,
1348
- builder_env,
1349
- force_build,
1350
- mlrun_version_specifier,
1351
- show_on_failure,
1352
- skip_deployed,
1353
- watch,
1354
- is_kfp,
1355
- with_mlrun,
1350
+ builder_env: dict,
1351
+ force_build: bool,
1352
+ mlrun_version_specifier: typing.Optional[bool],
1353
+ show_on_failure: bool,
1354
+ skip_deployed: bool,
1355
+ watch: bool,
1356
+ is_kfp: bool,
1357
+ with_mlrun: typing.Optional[bool],
1356
1358
  ):
1357
1359
  # When we're in pipelines context we must watch otherwise the pipelines pod will exit before the operation
1358
1360
  # is actually done. (when a pipelines pod exits, the pipeline step marked as done)
1359
1361
  if is_kfp:
1360
1362
  watch = True
1361
1363
 
1364
+ if skip_deployed and self.requires_build() and not self.is_deployed():
1365
+ logger.warning(
1366
+ f"Even though {skip_deployed=}, the build might be triggered due to the function's configuration. "
1367
+ "See requires_build() and is_deployed() for reasoning."
1368
+ )
1369
+
1362
1370
  db = self._get_db()
1363
1371
  data = db.remote_builder(
1364
1372
  self,
@@ -18,6 +18,7 @@ from mlrun_pipelines.mounts import mount_v3io, mount_v3iod
18
18
 
19
19
  import mlrun.common.schemas.function
20
20
  import mlrun.errors
21
+ import mlrun.k8s_utils
21
22
  import mlrun.runtimes.pod
22
23
  from mlrun.config import config
23
24
 
@@ -505,6 +506,7 @@ class Spark3Runtime(KubejobRuntime):
505
506
  raise NotImplementedError(
506
507
  "Setting node name is not supported for spark runtime"
507
508
  )
509
+ mlrun.k8s_utils.validate_node_selectors(node_selector, raise_on_error=False)
508
510
  self.with_driver_node_selection(node_name, node_selector, affinity, tolerations)
509
511
  self.with_executor_node_selection(
510
512
  node_name, node_selector, affinity, tolerations
@@ -537,6 +539,7 @@ class Spark3Runtime(KubejobRuntime):
537
539
  if affinity is not None:
538
540
  self.spec.driver_affinity = affinity
539
541
  if node_selector is not None:
542
+ mlrun.k8s_utils.validate_node_selectors(node_selector, raise_on_error=False)
540
543
  self.spec.driver_node_selector = node_selector
541
544
  if tolerations is not None:
542
545
  self.spec.driver_tolerations = tolerations
@@ -568,6 +571,7 @@ class Spark3Runtime(KubejobRuntime):
568
571
  if affinity is not None:
569
572
  self.spec.executor_affinity = affinity
570
573
  if node_selector is not None:
574
+ mlrun.k8s_utils.validate_node_selectors(node_selector, raise_on_error=False)
571
575
  self.spec.executor_node_selector = node_selector
572
576
  if tolerations is not None:
573
577
  self.spec.executor_tolerations = tolerations
mlrun/serving/server.py CHANGED
@@ -401,6 +401,8 @@ def v2_serving_handler(context, event, get_body=False):
401
401
  "kafka-cluster",
402
402
  "v3ioStream",
403
403
  "v3io-stream",
404
+ "rabbit-mq",
405
+ "rabbitMq",
404
406
  ):
405
407
  event.path = "/"
406
408