mlrun 1.6.4rc2__py3-none-any.whl → 1.7.0rc20__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.
- mlrun/__init__.py +11 -1
- mlrun/__main__.py +26 -112
- mlrun/alerts/__init__.py +15 -0
- mlrun/alerts/alert.py +144 -0
- mlrun/api/schemas/__init__.py +5 -4
- mlrun/artifacts/__init__.py +8 -3
- mlrun/artifacts/base.py +46 -257
- mlrun/artifacts/dataset.py +11 -192
- mlrun/artifacts/manager.py +47 -48
- mlrun/artifacts/model.py +31 -159
- mlrun/artifacts/plots.py +23 -380
- mlrun/common/constants.py +69 -0
- mlrun/common/db/sql_session.py +2 -3
- mlrun/common/formatters/__init__.py +19 -0
- mlrun/common/formatters/artifact.py +21 -0
- mlrun/common/formatters/base.py +78 -0
- mlrun/common/formatters/function.py +41 -0
- mlrun/common/formatters/pipeline.py +53 -0
- mlrun/common/formatters/project.py +51 -0
- mlrun/common/helpers.py +1 -2
- mlrun/common/model_monitoring/helpers.py +9 -5
- mlrun/{runtimes → common/runtimes}/constants.py +37 -9
- mlrun/common/schemas/__init__.py +24 -4
- mlrun/common/schemas/alert.py +203 -0
- mlrun/common/schemas/api_gateway.py +148 -0
- mlrun/common/schemas/artifact.py +18 -8
- mlrun/common/schemas/auth.py +11 -5
- mlrun/common/schemas/background_task.py +1 -1
- mlrun/common/schemas/client_spec.py +4 -1
- mlrun/common/schemas/feature_store.py +16 -16
- mlrun/common/schemas/frontend_spec.py +8 -7
- mlrun/common/schemas/function.py +5 -1
- mlrun/common/schemas/hub.py +11 -18
- mlrun/common/schemas/memory_reports.py +2 -2
- mlrun/common/schemas/model_monitoring/__init__.py +18 -3
- mlrun/common/schemas/model_monitoring/constants.py +83 -26
- mlrun/common/schemas/model_monitoring/grafana.py +13 -9
- mlrun/common/schemas/model_monitoring/model_endpoints.py +99 -16
- mlrun/common/schemas/notification.py +4 -4
- mlrun/common/schemas/object.py +2 -2
- mlrun/{runtimes/mpijob/v1alpha1.py → common/schemas/pagination.py} +10 -13
- mlrun/common/schemas/pipeline.py +1 -10
- mlrun/common/schemas/project.py +24 -23
- mlrun/common/schemas/runtime_resource.py +8 -12
- mlrun/common/schemas/schedule.py +3 -3
- mlrun/common/schemas/tag.py +1 -2
- mlrun/common/schemas/workflow.py +2 -2
- mlrun/common/types.py +7 -1
- mlrun/config.py +54 -17
- mlrun/data_types/to_pandas.py +10 -12
- mlrun/datastore/__init__.py +5 -8
- mlrun/datastore/alibaba_oss.py +130 -0
- mlrun/datastore/azure_blob.py +17 -5
- mlrun/datastore/base.py +62 -39
- mlrun/datastore/datastore.py +28 -9
- mlrun/datastore/datastore_profile.py +146 -20
- mlrun/datastore/filestore.py +0 -1
- mlrun/datastore/google_cloud_storage.py +6 -2
- mlrun/datastore/hdfs.py +56 -0
- mlrun/datastore/inmem.py +2 -2
- mlrun/datastore/redis.py +6 -2
- mlrun/datastore/s3.py +9 -0
- mlrun/datastore/snowflake_utils.py +43 -0
- mlrun/datastore/sources.py +201 -96
- mlrun/datastore/spark_utils.py +1 -2
- mlrun/datastore/store_resources.py +7 -7
- mlrun/datastore/targets.py +358 -104
- mlrun/datastore/utils.py +72 -58
- mlrun/datastore/v3io.py +5 -1
- mlrun/db/base.py +185 -35
- mlrun/db/factory.py +1 -1
- mlrun/db/httpdb.py +614 -179
- mlrun/db/nopdb.py +210 -26
- mlrun/errors.py +12 -1
- mlrun/execution.py +41 -24
- mlrun/feature_store/__init__.py +0 -2
- mlrun/feature_store/api.py +40 -72
- mlrun/feature_store/common.py +1 -1
- mlrun/feature_store/feature_set.py +76 -55
- mlrun/feature_store/feature_vector.py +28 -30
- mlrun/feature_store/ingestion.py +7 -6
- mlrun/feature_store/retrieval/base.py +16 -11
- mlrun/feature_store/retrieval/conversion.py +11 -13
- mlrun/feature_store/retrieval/dask_merger.py +2 -0
- mlrun/feature_store/retrieval/job.py +9 -3
- mlrun/feature_store/retrieval/local_merger.py +2 -0
- mlrun/feature_store/retrieval/spark_merger.py +34 -24
- mlrun/feature_store/steps.py +37 -34
- mlrun/features.py +9 -20
- mlrun/frameworks/_common/artifacts_library.py +9 -9
- mlrun/frameworks/_common/mlrun_interface.py +5 -5
- mlrun/frameworks/_common/model_handler.py +48 -48
- mlrun/frameworks/_common/plan.py +2 -3
- mlrun/frameworks/_common/producer.py +3 -4
- mlrun/frameworks/_common/utils.py +5 -5
- mlrun/frameworks/_dl_common/loggers/logger.py +6 -7
- mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +9 -9
- mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +23 -47
- mlrun/frameworks/_ml_common/artifacts_library.py +1 -2
- mlrun/frameworks/_ml_common/loggers/logger.py +3 -4
- mlrun/frameworks/_ml_common/loggers/mlrun_logger.py +4 -5
- mlrun/frameworks/_ml_common/model_handler.py +24 -24
- mlrun/frameworks/_ml_common/pkl_model_server.py +2 -2
- mlrun/frameworks/_ml_common/plan.py +1 -1
- mlrun/frameworks/_ml_common/plans/calibration_curve_plan.py +2 -3
- mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +2 -3
- mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
- mlrun/frameworks/_ml_common/plans/feature_importance_plan.py +3 -3
- mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
- mlrun/frameworks/_ml_common/utils.py +4 -4
- mlrun/frameworks/auto_mlrun/auto_mlrun.py +9 -9
- mlrun/frameworks/huggingface/model_server.py +4 -4
- mlrun/frameworks/lgbm/__init__.py +33 -33
- mlrun/frameworks/lgbm/callbacks/callback.py +2 -4
- mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -5
- mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -5
- mlrun/frameworks/lgbm/mlrun_interfaces/booster_mlrun_interface.py +1 -3
- mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +6 -6
- mlrun/frameworks/lgbm/model_handler.py +10 -10
- mlrun/frameworks/lgbm/model_server.py +6 -6
- mlrun/frameworks/lgbm/utils.py +5 -5
- mlrun/frameworks/onnx/dataset.py +8 -8
- mlrun/frameworks/onnx/mlrun_interface.py +3 -3
- mlrun/frameworks/onnx/model_handler.py +6 -6
- mlrun/frameworks/onnx/model_server.py +7 -7
- mlrun/frameworks/parallel_coordinates.py +4 -3
- mlrun/frameworks/pytorch/__init__.py +18 -18
- mlrun/frameworks/pytorch/callbacks/callback.py +4 -5
- mlrun/frameworks/pytorch/callbacks/logging_callback.py +17 -17
- mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +11 -11
- mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +23 -29
- mlrun/frameworks/pytorch/callbacks_handler.py +38 -38
- mlrun/frameworks/pytorch/mlrun_interface.py +20 -20
- mlrun/frameworks/pytorch/model_handler.py +17 -17
- mlrun/frameworks/pytorch/model_server.py +7 -7
- mlrun/frameworks/sklearn/__init__.py +13 -13
- mlrun/frameworks/sklearn/estimator.py +4 -4
- mlrun/frameworks/sklearn/metrics_library.py +14 -14
- mlrun/frameworks/sklearn/mlrun_interface.py +3 -6
- mlrun/frameworks/sklearn/model_handler.py +2 -2
- mlrun/frameworks/tf_keras/__init__.py +10 -7
- mlrun/frameworks/tf_keras/callbacks/logging_callback.py +15 -15
- mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +11 -11
- mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +19 -23
- mlrun/frameworks/tf_keras/mlrun_interface.py +9 -11
- mlrun/frameworks/tf_keras/model_handler.py +14 -14
- mlrun/frameworks/tf_keras/model_server.py +6 -6
- mlrun/frameworks/xgboost/__init__.py +13 -13
- mlrun/frameworks/xgboost/model_handler.py +6 -6
- mlrun/k8s_utils.py +14 -16
- mlrun/launcher/__init__.py +1 -1
- mlrun/launcher/base.py +16 -15
- mlrun/launcher/client.py +8 -6
- mlrun/launcher/factory.py +1 -1
- mlrun/launcher/local.py +17 -11
- mlrun/launcher/remote.py +16 -10
- mlrun/lists.py +7 -6
- mlrun/model.py +238 -73
- mlrun/model_monitoring/__init__.py +1 -1
- mlrun/model_monitoring/api.py +138 -315
- mlrun/model_monitoring/application.py +5 -296
- mlrun/model_monitoring/applications/__init__.py +24 -0
- mlrun/model_monitoring/applications/_application_steps.py +157 -0
- mlrun/model_monitoring/applications/base.py +282 -0
- mlrun/model_monitoring/applications/context.py +214 -0
- mlrun/model_monitoring/applications/evidently_base.py +211 -0
- mlrun/model_monitoring/applications/histogram_data_drift.py +349 -0
- mlrun/model_monitoring/applications/results.py +99 -0
- mlrun/model_monitoring/controller.py +104 -84
- mlrun/model_monitoring/controller_handler.py +13 -5
- mlrun/model_monitoring/db/__init__.py +18 -0
- mlrun/model_monitoring/{stores → db/stores}/__init__.py +43 -36
- mlrun/model_monitoring/db/stores/base/__init__.py +15 -0
- mlrun/model_monitoring/{stores/model_endpoint_store.py → db/stores/base/store.py} +64 -40
- mlrun/model_monitoring/db/stores/sqldb/__init__.py +13 -0
- mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +71 -0
- mlrun/model_monitoring/{stores → db/stores/sqldb}/models/base.py +109 -5
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +88 -0
- mlrun/model_monitoring/{stores/models/mysql.py → db/stores/sqldb/models/sqlite.py} +19 -13
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +684 -0
- mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +13 -0
- mlrun/model_monitoring/{stores/kv_model_endpoint_store.py → db/stores/v3io_kv/kv_store.py} +310 -165
- mlrun/model_monitoring/db/tsdb/__init__.py +100 -0
- mlrun/model_monitoring/db/tsdb/base.py +329 -0
- mlrun/model_monitoring/db/tsdb/helpers.py +30 -0
- mlrun/model_monitoring/db/tsdb/tdengine/__init__.py +15 -0
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +240 -0
- mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +45 -0
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +397 -0
- mlrun/model_monitoring/db/tsdb/v3io/__init__.py +15 -0
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +117 -0
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +630 -0
- mlrun/model_monitoring/evidently_application.py +6 -118
- mlrun/model_monitoring/features_drift_table.py +134 -106
- mlrun/model_monitoring/helpers.py +127 -28
- mlrun/model_monitoring/metrics/__init__.py +13 -0
- mlrun/model_monitoring/metrics/histogram_distance.py +127 -0
- mlrun/model_monitoring/model_endpoint.py +3 -2
- mlrun/model_monitoring/prometheus.py +1 -4
- mlrun/model_monitoring/stream_processing.py +62 -231
- mlrun/model_monitoring/tracking_policy.py +9 -2
- mlrun/model_monitoring/writer.py +152 -124
- mlrun/package/__init__.py +6 -6
- mlrun/package/context_handler.py +5 -5
- mlrun/package/packager.py +7 -7
- mlrun/package/packagers/default_packager.py +6 -6
- mlrun/package/packagers/numpy_packagers.py +15 -15
- mlrun/package/packagers/pandas_packagers.py +5 -5
- mlrun/package/packagers/python_standard_library_packagers.py +10 -10
- mlrun/package/packagers_manager.py +19 -23
- mlrun/package/utils/_formatter.py +6 -6
- mlrun/package/utils/_pickler.py +2 -2
- mlrun/package/utils/_supported_format.py +4 -4
- mlrun/package/utils/log_hint_utils.py +2 -2
- mlrun/package/utils/type_hint_utils.py +4 -9
- mlrun/platforms/__init__.py +11 -10
- mlrun/platforms/iguazio.py +24 -203
- mlrun/projects/operations.py +35 -21
- mlrun/projects/pipelines.py +68 -99
- mlrun/projects/project.py +830 -266
- mlrun/render.py +3 -11
- mlrun/run.py +162 -166
- mlrun/runtimes/__init__.py +62 -7
- mlrun/runtimes/base.py +39 -32
- mlrun/runtimes/daskjob.py +8 -8
- mlrun/runtimes/databricks_job/databricks_cancel_task.py +1 -1
- mlrun/runtimes/databricks_job/databricks_runtime.py +7 -7
- mlrun/runtimes/databricks_job/databricks_wrapper.py +1 -1
- mlrun/runtimes/funcdoc.py +0 -28
- mlrun/runtimes/function_reference.py +1 -1
- mlrun/runtimes/kubejob.py +28 -122
- mlrun/runtimes/local.py +6 -3
- mlrun/runtimes/mpijob/__init__.py +0 -20
- mlrun/runtimes/mpijob/abstract.py +9 -10
- mlrun/runtimes/mpijob/v1.py +1 -1
- mlrun/{model_monitoring/stores/models/sqlite.py → runtimes/nuclio/__init__.py} +7 -9
- mlrun/runtimes/nuclio/api_gateway.py +709 -0
- mlrun/runtimes/nuclio/application/__init__.py +15 -0
- mlrun/runtimes/nuclio/application/application.py +523 -0
- mlrun/runtimes/nuclio/application/reverse_proxy.go +95 -0
- mlrun/runtimes/{function.py → nuclio/function.py} +112 -73
- mlrun/runtimes/{nuclio.py → nuclio/nuclio.py} +6 -6
- mlrun/runtimes/{serving.py → nuclio/serving.py} +45 -51
- mlrun/runtimes/pod.py +286 -88
- mlrun/runtimes/remotesparkjob.py +2 -2
- mlrun/runtimes/sparkjob/spark3job.py +51 -34
- mlrun/runtimes/utils.py +7 -75
- mlrun/secrets.py +9 -5
- mlrun/serving/remote.py +2 -7
- mlrun/serving/routers.py +13 -10
- mlrun/serving/server.py +22 -26
- mlrun/serving/states.py +99 -25
- mlrun/serving/utils.py +3 -3
- mlrun/serving/v1_serving.py +6 -7
- mlrun/serving/v2_serving.py +59 -20
- mlrun/track/tracker.py +2 -1
- mlrun/track/tracker_manager.py +3 -3
- mlrun/track/trackers/mlflow_tracker.py +1 -2
- mlrun/utils/async_http.py +5 -7
- mlrun/utils/azure_vault.py +1 -1
- mlrun/utils/clones.py +1 -2
- mlrun/utils/condition_evaluator.py +3 -3
- mlrun/utils/db.py +3 -3
- mlrun/utils/helpers.py +183 -197
- mlrun/utils/http.py +2 -5
- mlrun/utils/logger.py +76 -14
- mlrun/utils/notifications/notification/__init__.py +17 -12
- mlrun/utils/notifications/notification/base.py +14 -2
- mlrun/utils/notifications/notification/console.py +2 -0
- mlrun/utils/notifications/notification/git.py +3 -1
- mlrun/utils/notifications/notification/ipython.py +3 -1
- mlrun/utils/notifications/notification/slack.py +101 -21
- mlrun/utils/notifications/notification/webhook.py +11 -1
- mlrun/utils/notifications/notification_pusher.py +155 -30
- mlrun/utils/retryer.py +208 -0
- mlrun/utils/singleton.py +1 -1
- mlrun/utils/v3io_clients.py +2 -4
- mlrun/utils/version/version.json +2 -2
- mlrun/utils/version/version.py +2 -6
- {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/METADATA +31 -19
- mlrun-1.7.0rc20.dist-info/RECORD +353 -0
- mlrun/kfpops.py +0 -868
- mlrun/model_monitoring/batch.py +0 -1095
- mlrun/model_monitoring/stores/models/__init__.py +0 -27
- mlrun/model_monitoring/stores/sql_model_endpoint_store.py +0 -384
- mlrun/platforms/other.py +0 -306
- mlrun-1.6.4rc2.dist-info/RECORD +0 -314
- {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/LICENSE +0 -0
- {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/WHEEL +0 -0
- {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/entry_points.txt +0 -0
- {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
|
|
15
|
+
from .application import ApplicationRuntime
|
|
@@ -0,0 +1,523 @@
|
|
|
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
|
+
import pathlib
|
|
15
|
+
import typing
|
|
16
|
+
|
|
17
|
+
import nuclio
|
|
18
|
+
|
|
19
|
+
import mlrun.common.schemas as schemas
|
|
20
|
+
import mlrun.errors
|
|
21
|
+
from mlrun.common.runtimes.constants import NuclioIngressAddTemplatedIngressModes
|
|
22
|
+
from mlrun.runtimes import RemoteRuntime
|
|
23
|
+
from mlrun.runtimes.nuclio import min_nuclio_versions
|
|
24
|
+
from mlrun.runtimes.nuclio.api_gateway import (
|
|
25
|
+
APIGateway,
|
|
26
|
+
APIGatewayMetadata,
|
|
27
|
+
APIGatewaySpec,
|
|
28
|
+
)
|
|
29
|
+
from mlrun.runtimes.nuclio.function import NuclioSpec, NuclioStatus
|
|
30
|
+
from mlrun.utils import logger
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ApplicationSpec(NuclioSpec):
|
|
34
|
+
_dict_fields = NuclioSpec._dict_fields + [
|
|
35
|
+
"internal_application_port",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
command=None,
|
|
41
|
+
args=None,
|
|
42
|
+
image=None,
|
|
43
|
+
mode=None,
|
|
44
|
+
entry_points=None,
|
|
45
|
+
description=None,
|
|
46
|
+
replicas=None,
|
|
47
|
+
min_replicas=None,
|
|
48
|
+
max_replicas=None,
|
|
49
|
+
volumes=None,
|
|
50
|
+
volume_mounts=None,
|
|
51
|
+
env=None,
|
|
52
|
+
resources=None,
|
|
53
|
+
config=None,
|
|
54
|
+
base_spec=None,
|
|
55
|
+
no_cache=None,
|
|
56
|
+
source=None,
|
|
57
|
+
image_pull_policy=None,
|
|
58
|
+
function_kind=None,
|
|
59
|
+
build=None,
|
|
60
|
+
service_account=None,
|
|
61
|
+
readiness_timeout=None,
|
|
62
|
+
readiness_timeout_before_failure=None,
|
|
63
|
+
default_handler=None,
|
|
64
|
+
node_name=None,
|
|
65
|
+
node_selector=None,
|
|
66
|
+
affinity=None,
|
|
67
|
+
disable_auto_mount=False,
|
|
68
|
+
priority_class_name=None,
|
|
69
|
+
pythonpath=None,
|
|
70
|
+
workdir=None,
|
|
71
|
+
image_pull_secret=None,
|
|
72
|
+
tolerations=None,
|
|
73
|
+
preemption_mode=None,
|
|
74
|
+
security_context=None,
|
|
75
|
+
service_type=None,
|
|
76
|
+
add_templated_ingress_host_mode=None,
|
|
77
|
+
clone_target_dir=None,
|
|
78
|
+
state_thresholds=None,
|
|
79
|
+
disable_default_http_trigger=None,
|
|
80
|
+
internal_application_port=None,
|
|
81
|
+
):
|
|
82
|
+
super().__init__(
|
|
83
|
+
command=command,
|
|
84
|
+
args=args,
|
|
85
|
+
image=image,
|
|
86
|
+
mode=mode,
|
|
87
|
+
entry_points=entry_points,
|
|
88
|
+
description=description,
|
|
89
|
+
replicas=replicas,
|
|
90
|
+
min_replicas=min_replicas,
|
|
91
|
+
max_replicas=max_replicas,
|
|
92
|
+
volumes=volumes,
|
|
93
|
+
volume_mounts=volume_mounts,
|
|
94
|
+
env=env,
|
|
95
|
+
resources=resources,
|
|
96
|
+
config=config,
|
|
97
|
+
base_spec=base_spec,
|
|
98
|
+
no_cache=no_cache,
|
|
99
|
+
source=source,
|
|
100
|
+
image_pull_policy=image_pull_policy,
|
|
101
|
+
function_kind=function_kind,
|
|
102
|
+
build=build,
|
|
103
|
+
service_account=service_account,
|
|
104
|
+
readiness_timeout=readiness_timeout,
|
|
105
|
+
readiness_timeout_before_failure=readiness_timeout_before_failure,
|
|
106
|
+
default_handler=default_handler,
|
|
107
|
+
node_name=node_name,
|
|
108
|
+
node_selector=node_selector,
|
|
109
|
+
affinity=affinity,
|
|
110
|
+
disable_auto_mount=disable_auto_mount,
|
|
111
|
+
priority_class_name=priority_class_name,
|
|
112
|
+
pythonpath=pythonpath,
|
|
113
|
+
workdir=workdir,
|
|
114
|
+
image_pull_secret=image_pull_secret,
|
|
115
|
+
tolerations=tolerations,
|
|
116
|
+
preemption_mode=preemption_mode,
|
|
117
|
+
security_context=security_context,
|
|
118
|
+
service_type=service_type,
|
|
119
|
+
add_templated_ingress_host_mode=add_templated_ingress_host_mode,
|
|
120
|
+
clone_target_dir=clone_target_dir,
|
|
121
|
+
state_thresholds=state_thresholds,
|
|
122
|
+
disable_default_http_trigger=disable_default_http_trigger,
|
|
123
|
+
)
|
|
124
|
+
self.internal_application_port = (
|
|
125
|
+
internal_application_port
|
|
126
|
+
or mlrun.mlconf.function.application.default_sidecar_internal_port
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def internal_application_port(self):
|
|
131
|
+
return self._internal_application_port
|
|
132
|
+
|
|
133
|
+
@internal_application_port.setter
|
|
134
|
+
def internal_application_port(self, port):
|
|
135
|
+
port = int(port)
|
|
136
|
+
if port < 0 or port > 65535:
|
|
137
|
+
raise ValueError("Port must be in the range 0-65535")
|
|
138
|
+
self._internal_application_port = port
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class ApplicationStatus(NuclioStatus):
|
|
142
|
+
def __init__(
|
|
143
|
+
self,
|
|
144
|
+
state=None,
|
|
145
|
+
nuclio_name=None,
|
|
146
|
+
address=None,
|
|
147
|
+
internal_invocation_urls=None,
|
|
148
|
+
external_invocation_urls=None,
|
|
149
|
+
build_pod=None,
|
|
150
|
+
container_image=None,
|
|
151
|
+
application_image=None,
|
|
152
|
+
sidecar_name=None,
|
|
153
|
+
api_gateway_name=None,
|
|
154
|
+
api_gateway=None,
|
|
155
|
+
url=None,
|
|
156
|
+
):
|
|
157
|
+
super().__init__(
|
|
158
|
+
state=state,
|
|
159
|
+
nuclio_name=nuclio_name,
|
|
160
|
+
address=address,
|
|
161
|
+
internal_invocation_urls=internal_invocation_urls,
|
|
162
|
+
external_invocation_urls=external_invocation_urls,
|
|
163
|
+
build_pod=build_pod,
|
|
164
|
+
container_image=container_image,
|
|
165
|
+
)
|
|
166
|
+
self.application_image = application_image or None
|
|
167
|
+
self.sidecar_name = sidecar_name or None
|
|
168
|
+
self.api_gateway_name = api_gateway_name or None
|
|
169
|
+
self.api_gateway = api_gateway or None
|
|
170
|
+
self.url = url or None
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class ApplicationRuntime(RemoteRuntime):
|
|
174
|
+
kind = "application"
|
|
175
|
+
|
|
176
|
+
@min_nuclio_versions("1.13.1")
|
|
177
|
+
def __init__(self, spec=None, metadata=None):
|
|
178
|
+
super().__init__(spec=spec, metadata=metadata)
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def spec(self) -> ApplicationSpec:
|
|
182
|
+
return self._spec
|
|
183
|
+
|
|
184
|
+
@spec.setter
|
|
185
|
+
def spec(self, spec):
|
|
186
|
+
self._spec = self._verify_dict(spec, "spec", ApplicationSpec)
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def status(self) -> ApplicationStatus:
|
|
190
|
+
return self._status
|
|
191
|
+
|
|
192
|
+
@status.setter
|
|
193
|
+
def status(self, status):
|
|
194
|
+
self._status = self._verify_dict(status, "status", ApplicationStatus)
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def api_gateway(self):
|
|
198
|
+
return self.status.api_gateway
|
|
199
|
+
|
|
200
|
+
@api_gateway.setter
|
|
201
|
+
def api_gateway(self, api_gateway: APIGateway):
|
|
202
|
+
self.status.api_gateway = api_gateway
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def url(self):
|
|
206
|
+
if not self.status.api_gateway:
|
|
207
|
+
self._sync_api_gateway()
|
|
208
|
+
return self.status.api_gateway.invoke_url
|
|
209
|
+
|
|
210
|
+
@url.setter
|
|
211
|
+
def url(self, url):
|
|
212
|
+
self.status.url = url
|
|
213
|
+
|
|
214
|
+
def set_internal_application_port(self, port: int):
|
|
215
|
+
self.spec.internal_application_port = port
|
|
216
|
+
|
|
217
|
+
def pre_deploy_validation(self):
|
|
218
|
+
super().pre_deploy_validation()
|
|
219
|
+
if not self.spec.config.get("spec.sidecars"):
|
|
220
|
+
raise mlrun.errors.MLRunBadRequestError(
|
|
221
|
+
"Application spec must include a sidecar configuration"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
sidecars = self.spec.config["spec.sidecars"]
|
|
225
|
+
for sidecar in sidecars:
|
|
226
|
+
if not sidecar.get("image"):
|
|
227
|
+
raise mlrun.errors.MLRunBadRequestError(
|
|
228
|
+
"Application sidecar spec must include an image"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
if not sidecar.get("ports"):
|
|
232
|
+
raise mlrun.errors.MLRunBadRequestError(
|
|
233
|
+
"Application sidecar spec must include at least one port"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
ports = sidecar["ports"]
|
|
237
|
+
for port in ports:
|
|
238
|
+
if not port.get("containerPort"):
|
|
239
|
+
raise mlrun.errors.MLRunBadRequestError(
|
|
240
|
+
"Application sidecar port spec must include a containerPort"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
if not port.get("name"):
|
|
244
|
+
raise mlrun.errors.MLRunBadRequestError(
|
|
245
|
+
"Application sidecar port spec must include a name"
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
if not sidecar.get("command") and sidecar.get("args"):
|
|
249
|
+
raise mlrun.errors.MLRunBadRequestError(
|
|
250
|
+
"Application sidecar spec must include a command if args are provided"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
def deploy(
|
|
254
|
+
self,
|
|
255
|
+
project="",
|
|
256
|
+
tag="",
|
|
257
|
+
verbose=False,
|
|
258
|
+
auth_info: schemas.AuthInfo = None,
|
|
259
|
+
builder_env: dict = None,
|
|
260
|
+
force_build: bool = False,
|
|
261
|
+
with_mlrun=None,
|
|
262
|
+
skip_deployed=False,
|
|
263
|
+
is_kfp=False,
|
|
264
|
+
mlrun_version_specifier=None,
|
|
265
|
+
show_on_failure: bool = False,
|
|
266
|
+
skip_access_key_auth: bool = False,
|
|
267
|
+
direct_port_access: bool = False,
|
|
268
|
+
authentication_mode: schemas.APIGatewayAuthenticationMode = None,
|
|
269
|
+
authentication_creds: tuple[str] = None,
|
|
270
|
+
):
|
|
271
|
+
"""
|
|
272
|
+
Deploy function, builds the application image if required (self.requires_build()) or force_build is True,
|
|
273
|
+
Once the image is built, the function is deployed.
|
|
274
|
+
:param project: Project name
|
|
275
|
+
:param tag: Function tag
|
|
276
|
+
:param verbose: Set True for verbose logging
|
|
277
|
+
:param auth_info: Service AuthInfo (deprecated and ignored)
|
|
278
|
+
:param builder_env: Env vars dict for source archive config/credentials
|
|
279
|
+
e.g. builder_env={"GIT_TOKEN": token}
|
|
280
|
+
:param force_build: Set True for force building the application image
|
|
281
|
+
:param with_mlrun: Add the current mlrun package to the container build
|
|
282
|
+
:param skip_deployed: Skip the build if we already have an image for the function
|
|
283
|
+
:param is_kfp: Deploy as part of a kfp pipeline
|
|
284
|
+
:param mlrun_version_specifier: Which mlrun package version to include (if not current)
|
|
285
|
+
:param show_on_failure: Show logs only in case of build failure
|
|
286
|
+
:param skip_access_key_auth: Skip adding access key auth to the API Gateway
|
|
287
|
+
:param direct_port_access: Set True to allow direct port access to the application sidecar
|
|
288
|
+
:param authentication_mode: API Gateway authentication mode
|
|
289
|
+
:param authentication_creds: API Gateway authentication credentials as a tuple (username, password)
|
|
290
|
+
:return: True if the function is ready (deployed)
|
|
291
|
+
"""
|
|
292
|
+
if self.requires_build() or force_build:
|
|
293
|
+
self._fill_credentials()
|
|
294
|
+
self._build_application_image(
|
|
295
|
+
builder_env=builder_env,
|
|
296
|
+
force_build=force_build,
|
|
297
|
+
watch=True,
|
|
298
|
+
with_mlrun=with_mlrun,
|
|
299
|
+
skip_deployed=skip_deployed,
|
|
300
|
+
is_kfp=is_kfp,
|
|
301
|
+
mlrun_version_specifier=mlrun_version_specifier,
|
|
302
|
+
show_on_failure=show_on_failure,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
self._ensure_reverse_proxy_configurations()
|
|
306
|
+
self._configure_application_sidecar()
|
|
307
|
+
|
|
308
|
+
# we only allow accessing the application via the API Gateway
|
|
309
|
+
name_tag = tag or self.metadata.tag
|
|
310
|
+
self.status.api_gateway_name = (
|
|
311
|
+
f"{self.metadata.name}-{name_tag}" if name_tag else self.metadata.name
|
|
312
|
+
)
|
|
313
|
+
self.spec.add_templated_ingress_host_mode = (
|
|
314
|
+
NuclioIngressAddTemplatedIngressModes.never
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
super().deploy(
|
|
318
|
+
project,
|
|
319
|
+
tag,
|
|
320
|
+
verbose,
|
|
321
|
+
auth_info,
|
|
322
|
+
builder_env,
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
ports = self.spec.internal_application_port if direct_port_access else []
|
|
326
|
+
self.create_api_gateway(
|
|
327
|
+
name=self.status.api_gateway_name,
|
|
328
|
+
ports=ports,
|
|
329
|
+
authentication_mode=authentication_mode,
|
|
330
|
+
authentication_creds=authentication_creds,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
def with_source_archive(
|
|
334
|
+
self, source, workdir=None, pull_at_runtime=True, target_dir=None
|
|
335
|
+
):
|
|
336
|
+
"""load the code from git/tar/zip archive at runtime or build
|
|
337
|
+
|
|
338
|
+
:param source: valid absolute path or URL to git, zip, or tar file, e.g.
|
|
339
|
+
git://github.com/mlrun/something.git
|
|
340
|
+
http://some/url/file.zip
|
|
341
|
+
note path source must exist on the image or exist locally when run is local
|
|
342
|
+
(it is recommended to use 'workdir' when source is a filepath instead)
|
|
343
|
+
:param workdir: working dir relative to the archive root (e.g. './subdir') or absolute to the image root
|
|
344
|
+
:param pull_at_runtime: load the archive into the container at job runtime vs on build/deploy
|
|
345
|
+
:param target_dir: target dir on runtime pod or repo clone / archive extraction
|
|
346
|
+
"""
|
|
347
|
+
self._configure_mlrun_build_with_source(
|
|
348
|
+
source=source,
|
|
349
|
+
workdir=workdir,
|
|
350
|
+
pull_at_runtime=pull_at_runtime,
|
|
351
|
+
target_dir=target_dir,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
@classmethod
|
|
355
|
+
def get_filename_and_handler(cls) -> (str, str):
|
|
356
|
+
reverse_proxy_file_path = pathlib.Path(__file__).parent / "reverse_proxy.go"
|
|
357
|
+
return str(reverse_proxy_file_path), "Handler"
|
|
358
|
+
|
|
359
|
+
def create_api_gateway(
|
|
360
|
+
self,
|
|
361
|
+
name: str = None,
|
|
362
|
+
path: str = None,
|
|
363
|
+
ports: list[int] = None,
|
|
364
|
+
authentication_mode: schemas.APIGatewayAuthenticationMode = None,
|
|
365
|
+
authentication_creds: tuple[str] = None,
|
|
366
|
+
):
|
|
367
|
+
api_gateway = APIGateway(
|
|
368
|
+
APIGatewayMetadata(
|
|
369
|
+
name=name,
|
|
370
|
+
namespace=self.metadata.namespace,
|
|
371
|
+
labels=self.metadata.labels,
|
|
372
|
+
annotations=self.metadata.annotations,
|
|
373
|
+
),
|
|
374
|
+
APIGatewaySpec(
|
|
375
|
+
functions=[self],
|
|
376
|
+
project=self.metadata.project,
|
|
377
|
+
path=path,
|
|
378
|
+
ports=mlrun.utils.helpers.as_list(ports) if ports else None,
|
|
379
|
+
),
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
authentication_mode = (
|
|
383
|
+
authentication_mode
|
|
384
|
+
or mlrun.mlconf.function.application.default_authentication_mode
|
|
385
|
+
)
|
|
386
|
+
if authentication_mode == schemas.APIGatewayAuthenticationMode.access_key:
|
|
387
|
+
api_gateway.with_access_key_auth()
|
|
388
|
+
elif authentication_mode == schemas.APIGatewayAuthenticationMode.basic:
|
|
389
|
+
api_gateway.with_basic_auth(*authentication_creds)
|
|
390
|
+
|
|
391
|
+
db = self._get_db()
|
|
392
|
+
api_gateway_scheme = db.store_api_gateway(
|
|
393
|
+
api_gateway=api_gateway.to_scheme(), project=self.metadata.project
|
|
394
|
+
)
|
|
395
|
+
if not self.status.api_gateway_name:
|
|
396
|
+
self.status.api_gateway_name = api_gateway_scheme.metadata.name
|
|
397
|
+
self.status.api_gateway = APIGateway.from_scheme(api_gateway_scheme)
|
|
398
|
+
self.status.api_gateway.wait_for_readiness()
|
|
399
|
+
self.url = self.status.api_gateway.invoke_url
|
|
400
|
+
|
|
401
|
+
def invoke(
|
|
402
|
+
self,
|
|
403
|
+
path: str,
|
|
404
|
+
body: typing.Union[str, bytes, dict] = None,
|
|
405
|
+
method: str = None,
|
|
406
|
+
headers: dict = None,
|
|
407
|
+
dashboard: str = "",
|
|
408
|
+
force_external_address: bool = False,
|
|
409
|
+
auth_info: schemas.AuthInfo = None,
|
|
410
|
+
mock: bool = None,
|
|
411
|
+
**http_client_kwargs,
|
|
412
|
+
):
|
|
413
|
+
self._sync_api_gateway()
|
|
414
|
+
# If the API Gateway is not ready or not set, try to invoke the function directly (without the API Gateway)
|
|
415
|
+
if not self.status.api_gateway:
|
|
416
|
+
super().invoke(
|
|
417
|
+
path,
|
|
418
|
+
body,
|
|
419
|
+
method,
|
|
420
|
+
headers,
|
|
421
|
+
dashboard,
|
|
422
|
+
force_external_address,
|
|
423
|
+
auth_info,
|
|
424
|
+
mock,
|
|
425
|
+
**http_client_kwargs,
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
credentials = (auth_info.username, auth_info.password) if auth_info else None
|
|
429
|
+
|
|
430
|
+
if not method:
|
|
431
|
+
method = "POST" if body else "GET"
|
|
432
|
+
return self.status.api_gateway.invoke(
|
|
433
|
+
method=method,
|
|
434
|
+
headers=headers,
|
|
435
|
+
credentials=credentials,
|
|
436
|
+
path=path,
|
|
437
|
+
**http_client_kwargs,
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
def _build_application_image(
|
|
441
|
+
self,
|
|
442
|
+
builder_env: dict = None,
|
|
443
|
+
force_build: bool = False,
|
|
444
|
+
watch=True,
|
|
445
|
+
with_mlrun=None,
|
|
446
|
+
skip_deployed=False,
|
|
447
|
+
is_kfp=False,
|
|
448
|
+
mlrun_version_specifier=None,
|
|
449
|
+
show_on_failure: bool = False,
|
|
450
|
+
):
|
|
451
|
+
if not self.spec.command:
|
|
452
|
+
logger.warning(
|
|
453
|
+
"Building the application image without a command. "
|
|
454
|
+
"Use spec.command and spec.args to specify the application entrypoint",
|
|
455
|
+
command=self.spec.command,
|
|
456
|
+
args=self.spec.args,
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
with_mlrun = self._resolve_build_with_mlrun(with_mlrun)
|
|
460
|
+
return self._build_image(
|
|
461
|
+
builder_env=builder_env,
|
|
462
|
+
force_build=force_build,
|
|
463
|
+
mlrun_version_specifier=mlrun_version_specifier,
|
|
464
|
+
show_on_failure=show_on_failure,
|
|
465
|
+
skip_deployed=skip_deployed,
|
|
466
|
+
watch=watch,
|
|
467
|
+
is_kfp=is_kfp,
|
|
468
|
+
with_mlrun=with_mlrun,
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
def _ensure_reverse_proxy_configurations(self):
|
|
472
|
+
if self.spec.build.functionSourceCode or self.status.container_image:
|
|
473
|
+
return
|
|
474
|
+
|
|
475
|
+
filename, handler = ApplicationRuntime.get_filename_and_handler()
|
|
476
|
+
name, spec, code = nuclio.build_file(
|
|
477
|
+
filename,
|
|
478
|
+
name=self.metadata.name,
|
|
479
|
+
handler=handler,
|
|
480
|
+
)
|
|
481
|
+
self.spec.function_handler = mlrun.utils.get_in(spec, "spec.handler")
|
|
482
|
+
self.spec.build.functionSourceCode = mlrun.utils.get_in(
|
|
483
|
+
spec, "spec.build.functionSourceCode"
|
|
484
|
+
)
|
|
485
|
+
self.spec.nuclio_runtime = mlrun.utils.get_in(spec, "spec.runtime")
|
|
486
|
+
|
|
487
|
+
def _configure_application_sidecar(self):
|
|
488
|
+
# Save the application image in the status to allow overriding it with the reverse proxy entry point
|
|
489
|
+
if self.spec.image and (
|
|
490
|
+
not self.status.application_image
|
|
491
|
+
or self.spec.image != self.status.container_image
|
|
492
|
+
):
|
|
493
|
+
self.status.application_image = self.spec.image
|
|
494
|
+
self.spec.image = ""
|
|
495
|
+
|
|
496
|
+
if self.status.container_image:
|
|
497
|
+
self.from_image(self.status.container_image)
|
|
498
|
+
# nuclio implementation detail - when providing the image and emptying out the source code,
|
|
499
|
+
# nuclio skips rebuilding the image and simply takes the prebuilt image
|
|
500
|
+
self.spec.build.functionSourceCode = ""
|
|
501
|
+
|
|
502
|
+
self.status.sidecar_name = f"{self.metadata.name}-sidecar"
|
|
503
|
+
self.with_sidecar(
|
|
504
|
+
name=self.status.sidecar_name,
|
|
505
|
+
image=self.status.application_image,
|
|
506
|
+
ports=self.spec.internal_application_port,
|
|
507
|
+
command=self.spec.command,
|
|
508
|
+
args=self.spec.args,
|
|
509
|
+
)
|
|
510
|
+
self.set_env("SIDECAR_PORT", self.spec.internal_application_port)
|
|
511
|
+
self.set_env("SIDECAR_HOST", "http://localhost")
|
|
512
|
+
|
|
513
|
+
def _sync_api_gateway(self):
|
|
514
|
+
if not self.status.api_gateway_name:
|
|
515
|
+
return
|
|
516
|
+
|
|
517
|
+
db = self._get_db()
|
|
518
|
+
api_gateway_scheme = db.get_api_gateway(
|
|
519
|
+
name=self.status.api_gateway_name, project=self.metadata.project
|
|
520
|
+
)
|
|
521
|
+
self.status.api_gateway = APIGateway.from_scheme(api_gateway_scheme)
|
|
522
|
+
self.status.api_gateway.wait_for_readiness()
|
|
523
|
+
self.url = self.status.api_gateway.invoke_url
|
|
@@ -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
|
+
}
|