mlrun 1.7.0rc4__py3-none-any.whl → 1.7.2__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 (235) hide show
  1. mlrun/__init__.py +11 -1
  2. mlrun/__main__.py +39 -121
  3. mlrun/{datastore/helpers.py → alerts/__init__.py} +2 -5
  4. mlrun/alerts/alert.py +248 -0
  5. mlrun/api/schemas/__init__.py +4 -3
  6. mlrun/artifacts/__init__.py +8 -3
  7. mlrun/artifacts/base.py +39 -254
  8. mlrun/artifacts/dataset.py +9 -190
  9. mlrun/artifacts/manager.py +73 -46
  10. mlrun/artifacts/model.py +30 -158
  11. mlrun/artifacts/plots.py +23 -380
  12. mlrun/common/constants.py +73 -1
  13. mlrun/common/db/sql_session.py +3 -2
  14. mlrun/common/formatters/__init__.py +21 -0
  15. mlrun/common/formatters/artifact.py +46 -0
  16. mlrun/common/formatters/base.py +113 -0
  17. mlrun/common/formatters/feature_set.py +44 -0
  18. mlrun/common/formatters/function.py +46 -0
  19. mlrun/common/formatters/pipeline.py +53 -0
  20. mlrun/common/formatters/project.py +51 -0
  21. mlrun/common/formatters/run.py +29 -0
  22. mlrun/common/helpers.py +11 -1
  23. mlrun/{runtimes → common/runtimes}/constants.py +32 -4
  24. mlrun/common/schemas/__init__.py +31 -4
  25. mlrun/common/schemas/alert.py +202 -0
  26. mlrun/common/schemas/api_gateway.py +196 -0
  27. mlrun/common/schemas/artifact.py +28 -1
  28. mlrun/common/schemas/auth.py +13 -2
  29. mlrun/common/schemas/client_spec.py +2 -1
  30. mlrun/common/schemas/common.py +7 -4
  31. mlrun/common/schemas/constants.py +3 -0
  32. mlrun/common/schemas/feature_store.py +58 -28
  33. mlrun/common/schemas/frontend_spec.py +8 -0
  34. mlrun/common/schemas/function.py +11 -0
  35. mlrun/common/schemas/hub.py +7 -9
  36. mlrun/common/schemas/model_monitoring/__init__.py +21 -4
  37. mlrun/common/schemas/model_monitoring/constants.py +136 -42
  38. mlrun/common/schemas/model_monitoring/grafana.py +9 -5
  39. mlrun/common/schemas/model_monitoring/model_endpoints.py +89 -41
  40. mlrun/common/schemas/notification.py +69 -12
  41. mlrun/{runtimes/mpijob/v1alpha1.py → common/schemas/pagination.py} +10 -13
  42. mlrun/common/schemas/pipeline.py +7 -0
  43. mlrun/common/schemas/project.py +67 -16
  44. mlrun/common/schemas/runs.py +17 -0
  45. mlrun/common/schemas/schedule.py +1 -1
  46. mlrun/common/schemas/workflow.py +10 -2
  47. mlrun/common/types.py +14 -1
  48. mlrun/config.py +233 -58
  49. mlrun/data_types/data_types.py +11 -1
  50. mlrun/data_types/spark.py +5 -4
  51. mlrun/data_types/to_pandas.py +75 -34
  52. mlrun/datastore/__init__.py +8 -10
  53. mlrun/datastore/alibaba_oss.py +131 -0
  54. mlrun/datastore/azure_blob.py +131 -43
  55. mlrun/datastore/base.py +107 -47
  56. mlrun/datastore/datastore.py +17 -7
  57. mlrun/datastore/datastore_profile.py +91 -7
  58. mlrun/datastore/dbfs_store.py +3 -7
  59. mlrun/datastore/filestore.py +1 -3
  60. mlrun/datastore/google_cloud_storage.py +92 -32
  61. mlrun/datastore/hdfs.py +5 -0
  62. mlrun/datastore/inmem.py +6 -3
  63. mlrun/datastore/redis.py +3 -2
  64. mlrun/datastore/s3.py +30 -12
  65. mlrun/datastore/snowflake_utils.py +45 -0
  66. mlrun/datastore/sources.py +274 -59
  67. mlrun/datastore/spark_utils.py +30 -0
  68. mlrun/datastore/store_resources.py +9 -7
  69. mlrun/datastore/storeytargets.py +151 -0
  70. mlrun/datastore/targets.py +387 -119
  71. mlrun/datastore/utils.py +68 -5
  72. mlrun/datastore/v3io.py +28 -50
  73. mlrun/db/auth_utils.py +152 -0
  74. mlrun/db/base.py +245 -20
  75. mlrun/db/factory.py +1 -4
  76. mlrun/db/httpdb.py +909 -231
  77. mlrun/db/nopdb.py +279 -14
  78. mlrun/errors.py +35 -5
  79. mlrun/execution.py +111 -38
  80. mlrun/feature_store/__init__.py +0 -2
  81. mlrun/feature_store/api.py +46 -53
  82. mlrun/feature_store/common.py +6 -11
  83. mlrun/feature_store/feature_set.py +48 -23
  84. mlrun/feature_store/feature_vector.py +13 -2
  85. mlrun/feature_store/ingestion.py +7 -6
  86. mlrun/feature_store/retrieval/base.py +9 -4
  87. mlrun/feature_store/retrieval/dask_merger.py +2 -0
  88. mlrun/feature_store/retrieval/job.py +13 -4
  89. mlrun/feature_store/retrieval/local_merger.py +2 -0
  90. mlrun/feature_store/retrieval/spark_merger.py +24 -32
  91. mlrun/feature_store/steps.py +38 -19
  92. mlrun/features.py +6 -14
  93. mlrun/frameworks/_common/plan.py +3 -3
  94. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +7 -12
  95. mlrun/frameworks/_ml_common/plan.py +1 -1
  96. mlrun/frameworks/auto_mlrun/auto_mlrun.py +2 -2
  97. mlrun/frameworks/lgbm/__init__.py +1 -1
  98. mlrun/frameworks/lgbm/callbacks/callback.py +2 -4
  99. mlrun/frameworks/lgbm/model_handler.py +1 -1
  100. mlrun/frameworks/parallel_coordinates.py +4 -4
  101. mlrun/frameworks/pytorch/__init__.py +2 -2
  102. mlrun/frameworks/sklearn/__init__.py +1 -1
  103. mlrun/frameworks/sklearn/mlrun_interface.py +13 -3
  104. mlrun/frameworks/tf_keras/__init__.py +5 -2
  105. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +1 -1
  106. mlrun/frameworks/tf_keras/mlrun_interface.py +2 -2
  107. mlrun/frameworks/xgboost/__init__.py +1 -1
  108. mlrun/k8s_utils.py +57 -12
  109. mlrun/launcher/__init__.py +1 -1
  110. mlrun/launcher/base.py +6 -5
  111. mlrun/launcher/client.py +13 -11
  112. mlrun/launcher/factory.py +1 -1
  113. mlrun/launcher/local.py +15 -5
  114. mlrun/launcher/remote.py +10 -3
  115. mlrun/lists.py +6 -2
  116. mlrun/model.py +297 -48
  117. mlrun/model_monitoring/__init__.py +1 -1
  118. mlrun/model_monitoring/api.py +152 -357
  119. mlrun/model_monitoring/applications/__init__.py +10 -0
  120. mlrun/model_monitoring/applications/_application_steps.py +190 -0
  121. mlrun/model_monitoring/applications/base.py +108 -0
  122. mlrun/model_monitoring/applications/context.py +341 -0
  123. mlrun/model_monitoring/{evidently_application.py → applications/evidently_base.py} +27 -22
  124. mlrun/model_monitoring/applications/histogram_data_drift.py +227 -91
  125. mlrun/model_monitoring/applications/results.py +99 -0
  126. mlrun/model_monitoring/controller.py +130 -303
  127. mlrun/model_monitoring/{stores/models/sqlite.py → db/__init__.py} +5 -10
  128. mlrun/model_monitoring/db/stores/__init__.py +136 -0
  129. mlrun/model_monitoring/db/stores/base/__init__.py +15 -0
  130. mlrun/model_monitoring/db/stores/base/store.py +213 -0
  131. mlrun/model_monitoring/db/stores/sqldb/__init__.py +13 -0
  132. mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +71 -0
  133. mlrun/model_monitoring/db/stores/sqldb/models/base.py +190 -0
  134. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +103 -0
  135. mlrun/model_monitoring/{stores/models/mysql.py → db/stores/sqldb/models/sqlite.py} +19 -13
  136. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +659 -0
  137. mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +13 -0
  138. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +726 -0
  139. mlrun/model_monitoring/db/tsdb/__init__.py +105 -0
  140. mlrun/model_monitoring/db/tsdb/base.py +448 -0
  141. mlrun/model_monitoring/db/tsdb/helpers.py +30 -0
  142. mlrun/model_monitoring/db/tsdb/tdengine/__init__.py +15 -0
  143. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +298 -0
  144. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +42 -0
  145. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +522 -0
  146. mlrun/model_monitoring/db/tsdb/v3io/__init__.py +15 -0
  147. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +158 -0
  148. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +849 -0
  149. mlrun/model_monitoring/features_drift_table.py +34 -22
  150. mlrun/model_monitoring/helpers.py +177 -39
  151. mlrun/model_monitoring/model_endpoint.py +3 -2
  152. mlrun/model_monitoring/stream_processing.py +165 -398
  153. mlrun/model_monitoring/tracking_policy.py +7 -1
  154. mlrun/model_monitoring/writer.py +161 -125
  155. mlrun/package/packagers/default_packager.py +2 -2
  156. mlrun/package/packagers_manager.py +1 -0
  157. mlrun/package/utils/_formatter.py +2 -2
  158. mlrun/platforms/__init__.py +11 -10
  159. mlrun/platforms/iguazio.py +67 -228
  160. mlrun/projects/__init__.py +6 -1
  161. mlrun/projects/operations.py +47 -20
  162. mlrun/projects/pipelines.py +396 -249
  163. mlrun/projects/project.py +1176 -406
  164. mlrun/render.py +28 -22
  165. mlrun/run.py +208 -181
  166. mlrun/runtimes/__init__.py +76 -11
  167. mlrun/runtimes/base.py +54 -24
  168. mlrun/runtimes/daskjob.py +9 -2
  169. mlrun/runtimes/databricks_job/databricks_runtime.py +1 -0
  170. mlrun/runtimes/databricks_job/databricks_wrapper.py +1 -1
  171. mlrun/runtimes/funcdoc.py +1 -29
  172. mlrun/runtimes/kubejob.py +34 -128
  173. mlrun/runtimes/local.py +39 -10
  174. mlrun/runtimes/mpijob/__init__.py +0 -20
  175. mlrun/runtimes/mpijob/abstract.py +8 -8
  176. mlrun/runtimes/mpijob/v1.py +1 -1
  177. mlrun/runtimes/nuclio/__init__.py +1 -0
  178. mlrun/runtimes/nuclio/api_gateway.py +769 -0
  179. mlrun/runtimes/nuclio/application/__init__.py +15 -0
  180. mlrun/runtimes/nuclio/application/application.py +758 -0
  181. mlrun/runtimes/nuclio/application/reverse_proxy.go +95 -0
  182. mlrun/runtimes/nuclio/function.py +188 -68
  183. mlrun/runtimes/nuclio/serving.py +57 -60
  184. mlrun/runtimes/pod.py +191 -58
  185. mlrun/runtimes/remotesparkjob.py +11 -8
  186. mlrun/runtimes/sparkjob/spark3job.py +17 -18
  187. mlrun/runtimes/utils.py +40 -73
  188. mlrun/secrets.py +6 -2
  189. mlrun/serving/__init__.py +8 -1
  190. mlrun/serving/remote.py +2 -3
  191. mlrun/serving/routers.py +89 -64
  192. mlrun/serving/server.py +54 -26
  193. mlrun/serving/states.py +187 -56
  194. mlrun/serving/utils.py +19 -11
  195. mlrun/serving/v2_serving.py +136 -63
  196. mlrun/track/tracker.py +2 -1
  197. mlrun/track/trackers/mlflow_tracker.py +5 -0
  198. mlrun/utils/async_http.py +26 -6
  199. mlrun/utils/db.py +18 -0
  200. mlrun/utils/helpers.py +375 -105
  201. mlrun/utils/http.py +2 -2
  202. mlrun/utils/logger.py +75 -9
  203. mlrun/utils/notifications/notification/__init__.py +14 -10
  204. mlrun/utils/notifications/notification/base.py +48 -0
  205. mlrun/utils/notifications/notification/console.py +2 -0
  206. mlrun/utils/notifications/notification/git.py +24 -1
  207. mlrun/utils/notifications/notification/ipython.py +2 -0
  208. mlrun/utils/notifications/notification/slack.py +96 -21
  209. mlrun/utils/notifications/notification/webhook.py +63 -2
  210. mlrun/utils/notifications/notification_pusher.py +146 -16
  211. mlrun/utils/regex.py +9 -0
  212. mlrun/utils/retryer.py +3 -2
  213. mlrun/utils/v3io_clients.py +2 -3
  214. mlrun/utils/version/version.json +2 -2
  215. mlrun-1.7.2.dist-info/METADATA +390 -0
  216. mlrun-1.7.2.dist-info/RECORD +351 -0
  217. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.2.dist-info}/WHEEL +1 -1
  218. mlrun/feature_store/retrieval/conversion.py +0 -271
  219. mlrun/kfpops.py +0 -868
  220. mlrun/model_monitoring/application.py +0 -310
  221. mlrun/model_monitoring/batch.py +0 -974
  222. mlrun/model_monitoring/controller_handler.py +0 -37
  223. mlrun/model_monitoring/prometheus.py +0 -216
  224. mlrun/model_monitoring/stores/__init__.py +0 -111
  225. mlrun/model_monitoring/stores/kv_model_endpoint_store.py +0 -574
  226. mlrun/model_monitoring/stores/model_endpoint_store.py +0 -145
  227. mlrun/model_monitoring/stores/models/__init__.py +0 -27
  228. mlrun/model_monitoring/stores/models/base.py +0 -84
  229. mlrun/model_monitoring/stores/sql_model_endpoint_store.py +0 -382
  230. mlrun/platforms/other.py +0 -305
  231. mlrun-1.7.0rc4.dist-info/METADATA +0 -269
  232. mlrun-1.7.0rc4.dist-info/RECORD +0 -321
  233. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.2.dist-info}/LICENSE +0 -0
  234. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.2.dist-info}/entry_points.txt +0 -0
  235. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,95 @@
1
+ // Copyright 2024 Iguazio
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ package main
15
+
16
+ import (
17
+ "bytes"
18
+ "fmt"
19
+ "net/http"
20
+ "net/http/httptest"
21
+ "net/http/httputil"
22
+ "net/url"
23
+ "os"
24
+ "strings"
25
+
26
+ nuclio "github.com/nuclio/nuclio-sdk-go"
27
+ )
28
+
29
+ func Handler(context *nuclio.Context, event nuclio.Event) (interface{}, error) {
30
+ reverseProxy := context.UserData.(map[string]interface{})["reverseProxy"].(*httputil.ReverseProxy)
31
+ sidecarUrl := context.UserData.(map[string]interface{})["server"].(string)
32
+
33
+ // populate reverse proxy http request
34
+ httpRequest, err := http.NewRequest(event.GetMethod(), event.GetPath(), bytes.NewReader(event.GetBody()))
35
+ if err != nil {
36
+ context.Logger.ErrorWith("Failed to create a reverse proxy request")
37
+ return nil, err
38
+ }
39
+ for k, v := range event.GetHeaders() {
40
+ httpRequest.Header[k] = []string{v.(string)}
41
+ }
42
+
43
+ // populate query params
44
+ query := httpRequest.URL.Query()
45
+ for k, v := range event.GetFields() {
46
+ query.Set(k, v.(string))
47
+ }
48
+ httpRequest.URL.RawQuery = query.Encode()
49
+
50
+ recorder := httptest.NewRecorder()
51
+ reverseProxy.ServeHTTP(recorder, httpRequest)
52
+
53
+ // send request to sidecar
54
+ context.Logger.DebugWith("Forwarding request to sidecar", "sidecarUrl", sidecarUrl, "query", httpRequest.URL.Query())
55
+ response := recorder.Result()
56
+
57
+ headers := make(map[string]interface{})
58
+ for key, value := range response.Header {
59
+ headers[key] = value[0]
60
+ }
61
+
62
+ // let the processor calculate the content length
63
+ delete(headers, "Content-Length")
64
+ return nuclio.Response{
65
+ StatusCode: response.StatusCode,
66
+ Body: recorder.Body.Bytes(),
67
+ ContentType: response.Header.Get("Content-Type"),
68
+ Headers: headers,
69
+ }, nil
70
+ }
71
+
72
+ func InitContext(context *nuclio.Context) error {
73
+ sidecarHost := os.Getenv("SIDECAR_HOST")
74
+ sidecarPort := os.Getenv("SIDECAR_PORT")
75
+ if sidecarHost == "" {
76
+ sidecarHost = "http://localhost"
77
+ } else if !strings.Contains(sidecarHost, "://") {
78
+ sidecarHost = fmt.Sprintf("http://%s", sidecarHost)
79
+ }
80
+
81
+ // url for request forwarding
82
+ sidecarUrl := fmt.Sprintf("%s:%s", sidecarHost, sidecarPort)
83
+ parsedURL, err := url.Parse(sidecarUrl)
84
+ if err != nil {
85
+ context.Logger.ErrorWith("Failed to parse sidecar url", "sidecarUrl", sidecarUrl)
86
+ return err
87
+ }
88
+ reverseProxy := httputil.NewSingleHostReverseProxy(parsedURL)
89
+
90
+ context.UserData = map[string]interface{}{
91
+ "server": sidecarUrl,
92
+ "reverseProxy": reverseProxy,
93
+ }
94
+ return nil
95
+ }
@@ -19,12 +19,16 @@ import warnings
19
19
  from datetime import datetime
20
20
  from time import sleep
21
21
 
22
+ import inflection
22
23
  import nuclio
23
24
  import nuclio.utils
24
25
  import requests
25
26
  import semver
26
27
  from aiohttp.client import ClientSession
27
28
  from kubernetes import client
29
+ from mlrun_pipelines.common.mounts import VolumeMount
30
+ from mlrun_pipelines.common.ops import deploy_op
31
+ from mlrun_pipelines.mounts import mount_v3io, v3io_cred
28
32
  from nuclio.deploy import find_dashboard_url, get_deploy_status
29
33
  from nuclio.triggers import V3IOStreamTrigger
30
34
 
@@ -36,15 +40,11 @@ import mlrun.utils.helpers
36
40
  from mlrun.common.schemas import AuthInfo
37
41
  from mlrun.config import config as mlconf
38
42
  from mlrun.errors import err_to_str
39
- from mlrun.kfpops import deploy_op
40
43
  from mlrun.lists import RunList
41
44
  from mlrun.model import RunObject
42
45
  from mlrun.platforms.iguazio import (
43
- VolumeMount,
44
- mount_v3io,
45
46
  parse_path,
46
47
  split_path,
47
- v3io_cred,
48
48
  )
49
49
  from mlrun.runtimes.base import FunctionStatus, RunError
50
50
  from mlrun.runtimes.pod import KubeResource, KubeResourceSpec
@@ -56,33 +56,9 @@ def validate_nuclio_version_compatibility(*min_versions):
56
56
  """
57
57
  :param min_versions: Valid minimum version(s) required, assuming no 2 versions has equal major and minor.
58
58
  """
59
- parsed_min_versions = [
60
- semver.VersionInfo.parse(min_version) for min_version in min_versions
61
- ]
62
- try:
63
- parsed_current_version = semver.VersionInfo.parse(mlconf.nuclio_version)
64
- except ValueError:
65
- # only log when version is set but invalid
66
- if mlconf.nuclio_version:
67
- logger.warning(
68
- "Unable to parse nuclio version, assuming compatibility",
69
- nuclio_version=mlconf.nuclio_version,
70
- min_versions=min_versions,
71
- )
72
- return True
73
-
74
- parsed_min_versions.sort(reverse=True)
75
- for parsed_min_version in parsed_min_versions:
76
- if (
77
- parsed_current_version.major == parsed_min_version.major
78
- and parsed_current_version.minor == parsed_min_version.minor
79
- and parsed_current_version.patch < parsed_min_version.patch
80
- ):
81
- return False
82
-
83
- if parsed_current_version >= parsed_min_version:
84
- return True
85
- return False
59
+ return mlrun.utils.helpers.validate_component_version_compatibility(
60
+ "nuclio", *min_versions
61
+ )
86
62
 
87
63
 
88
64
  def min_nuclio_versions(*versions):
@@ -91,9 +67,13 @@ def min_nuclio_versions(*versions):
91
67
  if validate_nuclio_version_compatibility(*versions):
92
68
  return function(*args, **kwargs)
93
69
 
70
+ if function.__name__ == "__init__":
71
+ name = inflection.titleize(function.__qualname__.split(".")[0])
72
+ else:
73
+ name = function.__qualname__
74
+
94
75
  message = (
95
- f"{function.__name__} is supported since nuclio {' or '.join(versions)}, currently using "
96
- f"nuclio {mlconf.nuclio_version}, please upgrade."
76
+ f"'{name}' function requires Nuclio v{' or v'.join(versions)} or higher"
97
77
  )
98
78
  raise mlrun.errors.MLRunIncompatibleVersionError(message)
99
79
 
@@ -291,6 +271,10 @@ class RemoteRuntime(KubeResource):
291
271
  def status(self, status):
292
272
  self._status = self._verify_dict(status, "status", NuclioStatus)
293
273
 
274
+ def pre_deploy_validation(self):
275
+ if self.metadata.tag:
276
+ mlrun.utils.validate_tag_name(self.metadata.tag, "function.metadata.tag")
277
+
294
278
  def set_config(self, key, value):
295
279
  self.spec.config[key] = value
296
280
  return self
@@ -313,10 +297,37 @@ class RemoteRuntime(KubeResource):
313
297
  """
314
298
  if hasattr(spec, "to_dict"):
315
299
  spec = spec.to_dict()
300
+
301
+ self._validate_triggers(spec)
302
+
316
303
  spec["name"] = name
317
304
  self.spec.config[f"spec.triggers.{name}"] = spec
318
305
  return self
319
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
+
320
331
  def with_source_archive(
321
332
  self,
322
333
  source,
@@ -342,17 +353,21 @@ class RemoteRuntime(KubeResource):
342
353
 
343
354
  git::
344
355
 
345
- fn.with_source_archive("git://github.com/org/repo#my-branch",
346
- handler="main:handler",
347
- workdir="path/inside/repo")
356
+ fn.with_source_archive(
357
+ "git://github.com/org/repo#my-branch",
358
+ handler="main:handler",
359
+ workdir="path/inside/repo",
360
+ )
348
361
 
349
362
  s3::
350
363
 
351
364
  fn.spec.nuclio_runtime = "golang"
352
- fn.with_source_archive("s3://my-bucket/path/in/bucket/my-functions-archive",
365
+ fn.with_source_archive(
366
+ "s3://my-bucket/path/in/bucket/my-functions-archive",
353
367
  handler="my_func:Handler",
354
368
  workdir="path/inside/functions/archive",
355
- runtime="golang")
369
+ runtime="golang",
370
+ )
356
371
  """
357
372
  self.spec.build.source = source
358
373
  # update handler in function_handler
@@ -431,14 +446,8 @@ class RemoteRuntime(KubeResource):
431
446
  raise ValueError(
432
447
  "gateway timeout must be greater than the worker timeout"
433
448
  )
434
- annotations["nginx.ingress.kubernetes.io/proxy-connect-timeout"] = (
435
- f"{gateway_timeout}"
436
- )
437
- annotations["nginx.ingress.kubernetes.io/proxy-read-timeout"] = (
438
- f"{gateway_timeout}"
439
- )
440
- annotations["nginx.ingress.kubernetes.io/proxy-send-timeout"] = (
441
- f"{gateway_timeout}"
449
+ mlrun.runtimes.utils.enrich_gateway_timeout_annotations(
450
+ annotations, gateway_timeout
442
451
  )
443
452
 
444
453
  trigger = nuclio.HttpTrigger(
@@ -459,6 +468,11 @@ class RemoteRuntime(KubeResource):
459
468
  return self
460
469
 
461
470
  def from_image(self, image):
471
+ """
472
+ Deploy the function with an existing nuclio processor image.
473
+
474
+ :param image: image name
475
+ """
462
476
  config = nuclio.config.new_config()
463
477
  update_in(
464
478
  config,
@@ -509,6 +523,11 @@ class RemoteRuntime(KubeResource):
509
523
  extra_attributes = extra_attributes or {}
510
524
  if ack_window_size:
511
525
  extra_attributes["ackWindowSize"] = ack_window_size
526
+
527
+ access_key = kwargs.pop("access_key", None)
528
+ if not access_key:
529
+ access_key = self._resolve_v3io_access_key()
530
+
512
531
  self.add_trigger(
513
532
  name,
514
533
  V3IOStreamTrigger(
@@ -520,11 +539,14 @@ class RemoteRuntime(KubeResource):
520
539
  webapi=endpoint or "http://v3io-webapi:8081",
521
540
  extra_attributes=extra_attributes,
522
541
  read_batch_size=256,
542
+ access_key=access_key,
523
543
  **kwargs,
524
544
  ),
525
545
  )
526
- self.spec.min_replicas = shards
527
- self.spec.max_replicas = shards
546
+ if self.spec.min_replicas != shards or self.spec.max_replicas != shards:
547
+ logger.warning(f"Setting function replicas to {shards}")
548
+ self.spec.min_replicas = shards
549
+ self.spec.max_replicas = shards
528
550
 
529
551
  def deploy(
530
552
  self,
@@ -540,11 +562,16 @@ class RemoteRuntime(KubeResource):
540
562
  :param project: project name
541
563
  :param tag: function tag
542
564
  :param verbose: set True for verbose logging
543
- :param auth_info: service AuthInfo
565
+ :param auth_info: service AuthInfo (deprecated and ignored)
544
566
  :param builder_env: env vars dict for source archive config/credentials e.g. builder_env={"GIT_TOKEN": token}
545
567
  :param force_build: set True for force building the image
546
568
  """
547
- # todo: verify that the function name is normalized
569
+ if auth_info:
570
+ # TODO: remove in 1.9.0
571
+ warnings.warn(
572
+ "'auth_info' is deprecated for nuclio runtimes in 1.7.0 and will be removed in 1.9.0",
573
+ FutureWarning,
574
+ )
548
575
 
549
576
  old_http_session = getattr(self, "_http_session", None)
550
577
  if old_http_session:
@@ -561,15 +588,12 @@ class RemoteRuntime(KubeResource):
561
588
  if tag:
562
589
  self.metadata.tag = tag
563
590
 
564
- save_record = False
565
591
  # Attempt auto-mounting, before sending to remote build
566
592
  self.try_auto_mount_based_on_config()
567
593
  self._fill_credentials()
568
594
  db = self._get_db()
569
595
  logger.info("Starting remote function deploy")
570
- data = db.remote_builder(
571
- self, False, builder_env=builder_env, force_build=force_build
572
- )
596
+ data = db.deploy_nuclio_function(func=self, builder_env=builder_env)
573
597
  self.status = data["data"].get("status")
574
598
  self._update_credentials_from_remote_build(data["data"])
575
599
 
@@ -577,19 +601,25 @@ class RemoteRuntime(KubeResource):
577
601
  # this also means that the function object will be updated with the function status
578
602
  self._wait_for_function_deployment(db, verbose=verbose)
579
603
 
604
+ return self._enrich_command_from_status()
605
+
606
+ def _enrich_command_from_status(self):
580
607
  # NOTE: on older mlrun versions & nuclio versions, function are exposed via NodePort
581
608
  # now, functions can be not exposed (using service type ClusterIP) and hence
582
609
  # for BC we first try to populate the external invocation url, and then
583
610
  # if not exists, take the internal invocation url
584
- if self.status.external_invocation_urls:
611
+ if (
612
+ self.status.external_invocation_urls
613
+ and self.status.external_invocation_urls[0] != ""
614
+ ):
585
615
  self.spec.command = f"http://{self.status.external_invocation_urls[0]}"
586
- save_record = True
587
- elif self.status.internal_invocation_urls:
616
+ elif (
617
+ self.status.internal_invocation_urls
618
+ and self.status.internal_invocation_urls[0] != ""
619
+ ):
588
620
  self.spec.command = f"http://{self.status.internal_invocation_urls[0]}"
589
- save_record = True
590
- elif self.status.address:
621
+ elif self.status.address and self.status.address != "":
591
622
  self.spec.command = f"http://{self.status.address}"
592
- save_record = True
593
623
 
594
624
  logger.info(
595
625
  "Successfully deployed function",
@@ -597,13 +627,11 @@ class RemoteRuntime(KubeResource):
597
627
  external_invocation_urls=self.status.external_invocation_urls,
598
628
  )
599
629
 
600
- if save_record:
601
- self.save(versioned=False)
630
+ self.save(versioned=False)
602
631
 
603
632
  return self.spec.command
604
633
 
605
634
  def _wait_for_function_deployment(self, db, verbose=False):
606
- text = ""
607
635
  state = ""
608
636
  last_log_timestamp = 1
609
637
  while state not in ["ready", "error", "unhealthy"]:
@@ -611,7 +639,7 @@ class RemoteRuntime(KubeResource):
611
639
  int(mlrun.mlconf.httpdb.logs.nuclio.pull_deploy_status_default_interval)
612
640
  )
613
641
  try:
614
- text, last_log_timestamp = db.get_builder_status(
642
+ text, last_log_timestamp = db.get_nuclio_deploy_status(
615
643
  self, last_log_timestamp=last_log_timestamp, verbose=verbose
616
644
  )
617
645
  except mlrun.db.RunDBError:
@@ -689,7 +717,7 @@ class RemoteRuntime(KubeResource):
689
717
  "State thresholds do not apply for nuclio as it has its own function pods healthiness monitoring"
690
718
  )
691
719
 
692
- @min_nuclio_versions("1.12.8")
720
+ @min_nuclio_versions("1.13.1")
693
721
  def disable_default_http_trigger(
694
722
  self,
695
723
  ):
@@ -698,7 +726,7 @@ class RemoteRuntime(KubeResource):
698
726
  """
699
727
  self.spec.disable_default_http_trigger = True
700
728
 
701
- @min_nuclio_versions("1.12.8")
729
+ @min_nuclio_versions("1.13.1")
702
730
  def enable_default_http_trigger(
703
731
  self,
704
732
  ):
@@ -707,6 +735,10 @@ class RemoteRuntime(KubeResource):
707
735
  """
708
736
  self.spec.disable_default_http_trigger = False
709
737
 
738
+ def skip_image_enrichment(self):
739
+ # make sure the API does not enrich the base image if the function is not a python function
740
+ return self.spec.nuclio_runtime and "python" not in self.spec.nuclio_runtime
741
+
710
742
  def _get_state(
711
743
  self,
712
744
  dashboard="",
@@ -749,7 +781,7 @@ class RemoteRuntime(KubeResource):
749
781
  return state, text, last_log_timestamp
750
782
 
751
783
  try:
752
- text, last_log_timestamp = self._get_db().get_builder_status(
784
+ text, last_log_timestamp = self._get_db().get_nuclio_deploy_status(
753
785
  self, last_log_timestamp=last_log_timestamp, verbose=verbose
754
786
  )
755
787
  except mlrun.db.RunDBError:
@@ -769,10 +801,13 @@ class RemoteRuntime(KubeResource):
769
801
  runtime_env["MLRUN_NAMESPACE"] = mlconf.namespace
770
802
  if self.metadata.credentials.access_key:
771
803
  runtime_env[
772
- mlrun.runtimes.constants.FunctionEnvironmentVariables.auth_session
804
+ mlrun.common.runtimes.constants.FunctionEnvironmentVariables.auth_session
773
805
  ] = self.metadata.credentials.access_key
774
806
  return runtime_env
775
807
 
808
+ def _get_serving_spec(self):
809
+ return None
810
+
776
811
  def _get_nuclio_config_spec_env(self):
777
812
  env_dict = {}
778
813
  external_source_env_dict = {}
@@ -958,6 +993,64 @@ class RemoteRuntime(KubeResource):
958
993
  data = json.loads(data)
959
994
  return data
960
995
 
996
+ def with_sidecar(
997
+ self,
998
+ name: str = None,
999
+ image: str = None,
1000
+ ports: typing.Optional[typing.Union[int, list[int]]] = None,
1001
+ command: typing.Optional[str] = None,
1002
+ args: typing.Optional[list[str]] = None,
1003
+ ):
1004
+ """
1005
+ Add a sidecar container to the function pod
1006
+
1007
+ :param name: Sidecar container name.
1008
+ :param image: Sidecar container image.
1009
+ :param ports: Sidecar container ports to expose. Can be a single port or a list of ports.
1010
+ :param command: Sidecar container command instead of the image entrypoint.
1011
+ :param args: Sidecar container command args (requires command to be set).
1012
+ """
1013
+ name = name or f"{self.metadata.name}-sidecar"
1014
+ sidecar = self._set_sidecar(name)
1015
+ if image:
1016
+ sidecar["image"] = image
1017
+
1018
+ ports = mlrun.utils.helpers.as_list(ports)
1019
+ # according to RFC-6335, port name should be less than 15 characters,
1020
+ # so we truncate it if needed and leave room for the index
1021
+ port_name = name[:13].rstrip("-_") if len(name) > 13 else name
1022
+ sidecar["ports"] = [
1023
+ {
1024
+ "name": f"{port_name}-{i}",
1025
+ "containerPort": port,
1026
+ "protocol": "TCP",
1027
+ }
1028
+ for i, port in enumerate(ports)
1029
+ ]
1030
+
1031
+ # if it is a redeploy, 'command' might be set with the previous invocation url.
1032
+ # in this case, we don't want to use it as the sidecar command
1033
+ if command and not command.startswith("http"):
1034
+ sidecar["command"] = mlrun.utils.helpers.as_list(command)
1035
+
1036
+ if args and sidecar.get("command"):
1037
+ sidecar["args"] = mlrun.utils.helpers.as_list(args)
1038
+
1039
+ # put the configured resources on the sidecar container instead of the reverse proxy container
1040
+ if self.spec.resources:
1041
+ sidecar["resources"] = self.spec.resources
1042
+ self.spec.resources = None
1043
+
1044
+ def _set_sidecar(self, name: str) -> dict:
1045
+ self.spec.config.setdefault("spec.sidecars", [])
1046
+ sidecars = self.spec.config["spec.sidecars"]
1047
+ for sidecar in sidecars:
1048
+ if sidecar["name"] == name:
1049
+ return sidecar
1050
+
1051
+ sidecars.append({"name": name})
1052
+ return sidecars[-1]
1053
+
961
1054
  def _trigger_of_kind_exists(self, kind: str) -> bool:
962
1055
  if not self.spec.config:
963
1056
  return False
@@ -1184,6 +1277,13 @@ class RemoteRuntime(KubeResource):
1184
1277
 
1185
1278
  return self._resolve_invocation_url("", force_external_address)
1186
1279
 
1280
+ @staticmethod
1281
+ def _resolve_v3io_access_key():
1282
+ # Nuclio supports generating access key for v3io stream trigger only from version 1.13.11
1283
+ if validate_nuclio_version_compatibility("1.13.11"):
1284
+ return mlrun.model.Credentials.generate_access_key
1285
+ return None
1286
+
1187
1287
 
1188
1288
  def parse_logs(logs):
1189
1289
  logs = json.loads(logs)
@@ -1278,3 +1378,23 @@ def get_nuclio_deploy_status(
1278
1378
  else:
1279
1379
  text = "\n".join(outputs) if outputs else ""
1280
1380
  return state, address, name, last_log_timestamp, text, function_status
1381
+
1382
+
1383
+ def enrich_nuclio_function_from_headers(
1384
+ func: RemoteRuntime,
1385
+ headers: dict,
1386
+ ):
1387
+ func.status.state = headers.get("x-mlrun-function-status", "")
1388
+ func.status.address = headers.get("x-mlrun-address", "")
1389
+ func.status.nuclio_name = headers.get("x-mlrun-name", "")
1390
+ func.status.internal_invocation_urls = (
1391
+ headers.get("x-mlrun-internal-invocation-urls", "").split(",")
1392
+ if headers.get("x-mlrun-internal-invocation-urls")
1393
+ else []
1394
+ )
1395
+ func.status.external_invocation_urls = (
1396
+ headers.get("x-mlrun-external-invocation-urls", "").split(",")
1397
+ if headers.get("x-mlrun-external-invocation-urls")
1398
+ else []
1399
+ )
1400
+ func.status.container_image = headers.get("x-mlrun-container-image", "")