mlrun 1.5.0rc5__py3-none-any.whl → 1.5.0rc6__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.
- mlrun/api/api/endpoints/datastore_profile.py +35 -13
- mlrun/api/api/endpoints/frontend_spec.py +1 -10
- mlrun/api/api/endpoints/functions.py +1 -1
- mlrun/api/api/endpoints/hub.py +2 -6
- mlrun/api/crud/client_spec.py +3 -0
- mlrun/api/crud/datastore_profiles.py +2 -2
- mlrun/api/crud/hub.py +158 -142
- mlrun/api/crud/workflows.py +7 -3
- mlrun/api/db/sqldb/db.py +19 -21
- mlrun/api/db/sqldb/models/models_mysql.py +10 -1
- mlrun/api/db/sqldb/models/models_sqlite.py +11 -1
- mlrun/api/initial_data.py +3 -5
- mlrun/api/launcher.py +2 -1
- mlrun/api/migrations_mysql/versions/026c947c4487_altering_table_datastore_profiles_2.py +46 -0
- mlrun/api/migrations_sqlite/versions/026c947c4487_altering_table_datastore_profiles_2.py +46 -0
- mlrun/api/rundb/sqldb.py +15 -9
- mlrun/api/utils/db/sqlite_migration.py +1 -0
- mlrun/common/model_monitoring/helpers.py +3 -1
- mlrun/common/schemas/client_spec.py +1 -0
- mlrun/common/schemas/datastore_profile.py +1 -1
- mlrun/common/schemas/frontend_spec.py +1 -1
- mlrun/config.py +3 -2
- mlrun/datastore/datastore_profile.py +33 -21
- mlrun/datastore/dbfs_store.py +4 -3
- mlrun/datastore/redis.py +6 -0
- mlrun/datastore/targets.py +12 -1
- mlrun/db/base.py +1 -1
- mlrun/db/httpdb.py +10 -9
- mlrun/db/nopdb.py +1 -1
- mlrun/feature_store/api.py +4 -1
- mlrun/feature_store/feature_set.py +3 -1
- mlrun/feature_store/ingestion.py +1 -0
- mlrun/launcher/base.py +1 -1
- mlrun/model.py +7 -5
- mlrun/projects/pipelines.py +7 -6
- mlrun/projects/project.py +2 -2
- mlrun/run.py +1 -1
- mlrun/runtimes/__init__.py +1 -0
- mlrun/utils/helpers.py +1 -1
- mlrun/utils/notifications/notification/webhook.py +9 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.5.0rc5.dist-info → mlrun-1.5.0rc6.dist-info}/METADATA +6 -5
- {mlrun-1.5.0rc5.dist-info → mlrun-1.5.0rc6.dist-info}/RECORD +47 -45
- {mlrun-1.5.0rc5.dist-info → mlrun-1.5.0rc6.dist-info}/LICENSE +0 -0
- {mlrun-1.5.0rc5.dist-info → mlrun-1.5.0rc6.dist-info}/WHEEL +0 -0
- {mlrun-1.5.0rc5.dist-info → mlrun-1.5.0rc6.dist-info}/entry_points.txt +0 -0
- {mlrun-1.5.0rc5.dist-info → mlrun-1.5.0rc6.dist-info}/top_level.txt +0 -0
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
#
|
|
15
15
|
|
|
16
|
+
from http import HTTPStatus
|
|
16
17
|
|
|
17
18
|
from fastapi import APIRouter, Depends
|
|
18
19
|
from fastapi.concurrency import run_in_threadpool
|
|
@@ -24,12 +25,13 @@ import mlrun.api.crud
|
|
|
24
25
|
import mlrun.api.utils.auth.verifier
|
|
25
26
|
import mlrun.api.utils.singletons.db
|
|
26
27
|
import mlrun.common.schemas
|
|
28
|
+
from mlrun.api.api.utils import log_and_raise
|
|
27
29
|
|
|
28
30
|
router = APIRouter()
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
@router.put(
|
|
32
|
-
path="/projects/{project_name}/
|
|
34
|
+
path="/projects/{project_name}/datastore-profiles",
|
|
33
35
|
)
|
|
34
36
|
async def store_datastore_profile(
|
|
35
37
|
project_name: str,
|
|
@@ -45,13 +47,20 @@ async def store_datastore_profile(
|
|
|
45
47
|
project_name,
|
|
46
48
|
auth_info.session,
|
|
47
49
|
)
|
|
48
|
-
await mlrun.api.utils.auth.verifier.AuthVerifier().
|
|
50
|
+
await mlrun.api.utils.auth.verifier.AuthVerifier().query_project_resource_permissions(
|
|
49
51
|
mlrun.common.schemas.AuthorizationResourceTypes.datastore_profile,
|
|
50
|
-
|
|
52
|
+
project_name,
|
|
53
|
+
info.name,
|
|
54
|
+
mlrun.common.schemas.AuthorizationAction.store,
|
|
51
55
|
auth_info,
|
|
52
56
|
)
|
|
53
57
|
# overwrite the project
|
|
54
|
-
info.project
|
|
58
|
+
if info.project != project_name:
|
|
59
|
+
log_and_raise(
|
|
60
|
+
HTTPStatus.BAD_REQUEST.value,
|
|
61
|
+
reason="The project name provided in the URI does not match the one specified in the DatastoreProfile",
|
|
62
|
+
)
|
|
63
|
+
|
|
55
64
|
await run_in_threadpool(
|
|
56
65
|
mlrun.api.utils.singletons.db.get_db().store_datastore_profile,
|
|
57
66
|
db_session,
|
|
@@ -67,7 +76,7 @@ async def store_datastore_profile(
|
|
|
67
76
|
|
|
68
77
|
|
|
69
78
|
@router.get(
|
|
70
|
-
path="/projects/{project_name}/
|
|
79
|
+
path="/projects/{project_name}/datastore-profiles",
|
|
71
80
|
)
|
|
72
81
|
async def list_datastore_profiles(
|
|
73
82
|
project_name: str,
|
|
@@ -82,20 +91,29 @@ async def list_datastore_profiles(
|
|
|
82
91
|
project_name,
|
|
83
92
|
auth_info.session,
|
|
84
93
|
)
|
|
85
|
-
await mlrun.api.utils.auth.verifier.AuthVerifier().
|
|
86
|
-
|
|
94
|
+
await mlrun.api.utils.auth.verifier.AuthVerifier().query_project_permissions(
|
|
95
|
+
project_name,
|
|
87
96
|
mlrun.common.schemas.AuthorizationAction.read,
|
|
88
97
|
auth_info,
|
|
89
98
|
)
|
|
90
|
-
|
|
99
|
+
profiles = await run_in_threadpool(
|
|
91
100
|
mlrun.api.utils.singletons.db.get_db().list_datastore_profiles,
|
|
92
101
|
db_session,
|
|
93
102
|
project_name,
|
|
94
103
|
)
|
|
104
|
+
if len(profiles) == 0:
|
|
105
|
+
return profiles
|
|
106
|
+
filtered_data = await mlrun.api.utils.auth.verifier.AuthVerifier().filter_project_resources_by_permissions(
|
|
107
|
+
mlrun.common.schemas.AuthorizationResourceTypes.datastore_profile,
|
|
108
|
+
profiles,
|
|
109
|
+
lambda profile: (project_name, profile.name),
|
|
110
|
+
auth_info,
|
|
111
|
+
)
|
|
112
|
+
return filtered_data
|
|
95
113
|
|
|
96
114
|
|
|
97
115
|
@router.get(
|
|
98
|
-
path="/projects/{project_name}/
|
|
116
|
+
path="/projects/{project_name}/datastore-profiles/{profile}",
|
|
99
117
|
)
|
|
100
118
|
async def get_datastore_profile(
|
|
101
119
|
project_name: str,
|
|
@@ -111,8 +129,10 @@ async def get_datastore_profile(
|
|
|
111
129
|
project_name,
|
|
112
130
|
auth_info.session,
|
|
113
131
|
)
|
|
114
|
-
await mlrun.api.utils.auth.verifier.AuthVerifier().
|
|
132
|
+
await mlrun.api.utils.auth.verifier.AuthVerifier().query_project_resource_permissions(
|
|
115
133
|
mlrun.common.schemas.AuthorizationResourceTypes.datastore_profile,
|
|
134
|
+
project_name,
|
|
135
|
+
profile,
|
|
116
136
|
mlrun.common.schemas.AuthorizationAction.read,
|
|
117
137
|
auth_info,
|
|
118
138
|
)
|
|
@@ -125,7 +145,7 @@ async def get_datastore_profile(
|
|
|
125
145
|
|
|
126
146
|
|
|
127
147
|
@router.delete(
|
|
128
|
-
path="/projects/{project_name}/
|
|
148
|
+
path="/projects/{project_name}/datastore-profiles/{profile}",
|
|
129
149
|
)
|
|
130
150
|
async def delete_datastore_profile(
|
|
131
151
|
project_name: str,
|
|
@@ -141,9 +161,11 @@ async def delete_datastore_profile(
|
|
|
141
161
|
project_name,
|
|
142
162
|
auth_info.session,
|
|
143
163
|
)
|
|
144
|
-
await mlrun.api.utils.auth.verifier.AuthVerifier().
|
|
164
|
+
await mlrun.api.utils.auth.verifier.AuthVerifier().query_project_resource_permissions(
|
|
145
165
|
mlrun.common.schemas.AuthorizationResourceTypes.datastore_profile,
|
|
146
|
-
|
|
166
|
+
project_name,
|
|
167
|
+
profile,
|
|
168
|
+
mlrun.common.schemas.AuthorizationAction.delete,
|
|
147
169
|
auth_info,
|
|
148
170
|
)
|
|
149
171
|
return await run_in_threadpool(
|
|
@@ -73,7 +73,7 @@ def get_frontend_spec(
|
|
|
73
73
|
function_deployment_target_image_template=function_deployment_target_image_template,
|
|
74
74
|
function_deployment_target_image_name_prefix_template=function_target_image_name_prefix_template,
|
|
75
75
|
function_deployment_target_image_registries_to_enforce_prefix=registries_to_enforce_prefix,
|
|
76
|
-
|
|
76
|
+
function_deployment_mlrun_requirement=mlrun.api.utils.builder.resolve_mlrun_install_command_version(),
|
|
77
77
|
auto_mount_type=config.storage.auto_mount_type,
|
|
78
78
|
auto_mount_params=config.get_storage_auto_mount_params(),
|
|
79
79
|
default_artifact_path=config.artifact_path,
|
|
@@ -87,15 +87,6 @@ def get_frontend_spec(
|
|
|
87
87
|
)
|
|
88
88
|
|
|
89
89
|
|
|
90
|
-
def _resolve_function_deployment_mlrun_command():
|
|
91
|
-
# TODO: When UI adds a requirements section, mlrun should be specified there instead of the commands section i.e.
|
|
92
|
-
# frontend spec will contain only the mlrun_version_specifier instead of the full command
|
|
93
|
-
mlrun_version_specifier = (
|
|
94
|
-
mlrun.api.utils.builder.resolve_mlrun_install_command_version()
|
|
95
|
-
)
|
|
96
|
-
return f'python -m pip install "{mlrun_version_specifier}"'
|
|
97
|
-
|
|
98
|
-
|
|
99
90
|
def _resolve_jobs_dashboard_url(session: str) -> typing.Optional[str]:
|
|
100
91
|
iguazio_client = mlrun.api.utils.clients.iguazio.Client()
|
|
101
92
|
grafana_service_url = iguazio_client.try_get_grafana_service_url(session)
|
|
@@ -267,7 +267,7 @@ async def build_function(
|
|
|
267
267
|
except ValueError:
|
|
268
268
|
log_and_raise(HTTPStatus.BAD_REQUEST.value, reason="bad JSON body")
|
|
269
269
|
|
|
270
|
-
logger.info(
|
|
270
|
+
logger.info("Building function", data=data)
|
|
271
271
|
function = data.get("function")
|
|
272
272
|
project = function.get("metadata", {}).get("project", mlrun.mlconf.default_project)
|
|
273
273
|
function_name = function.get("metadata", {}).get("name")
|
mlrun/api/api/endpoints/hub.py
CHANGED
|
@@ -81,13 +81,9 @@ async def list_sources(
|
|
|
81
81
|
auth_info,
|
|
82
82
|
)
|
|
83
83
|
|
|
84
|
-
hub_sources = await run_in_threadpool(
|
|
85
|
-
mlrun.api.utils.singletons.db.get_db().list_hub_sources, db_session
|
|
86
|
-
)
|
|
87
|
-
|
|
88
84
|
return await run_in_threadpool(
|
|
89
|
-
mlrun.api.crud.Hub().
|
|
90
|
-
|
|
85
|
+
mlrun.api.crud.Hub().list_hub_sources,
|
|
86
|
+
db_session,
|
|
91
87
|
item_name,
|
|
92
88
|
tag,
|
|
93
89
|
version,
|
mlrun/api/crud/client_spec.py
CHANGED
|
@@ -104,6 +104,9 @@ class ClientSpec(
|
|
|
104
104
|
model_endpoint_monitoring_store_type=self._get_config_value_if_not_default(
|
|
105
105
|
"model_endpoint_monitoring.store_type"
|
|
106
106
|
),
|
|
107
|
+
model_endpoint_monitoring_endpoint_store_connection=self._get_config_value_if_not_default(
|
|
108
|
+
"model_endpoint_monitoring.endpoint_store_connection"
|
|
109
|
+
),
|
|
107
110
|
packagers=self._get_config_value_if_not_default("packagers"),
|
|
108
111
|
)
|
|
109
112
|
|
|
@@ -37,7 +37,7 @@ class DatastoreProfiles(
|
|
|
37
37
|
def _store_secret(self, project, profile_name, profile_secret_json):
|
|
38
38
|
if not self._in_k8s():
|
|
39
39
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
40
|
-
"MLRun is not configured with k8s,
|
|
40
|
+
"MLRun is not configured with k8s, datastore profile credentials cannot be stored securely"
|
|
41
41
|
)
|
|
42
42
|
|
|
43
43
|
adjusted_secret = {
|
|
@@ -56,7 +56,7 @@ class DatastoreProfiles(
|
|
|
56
56
|
def _delete_secret(self, project, profile_name):
|
|
57
57
|
if not self._in_k8s():
|
|
58
58
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
59
|
-
"MLRun is not configured with k8s,
|
|
59
|
+
"MLRun is not configured with k8s, datastore profile credentials cannot be deleted"
|
|
60
60
|
)
|
|
61
61
|
|
|
62
62
|
adjusted_secret = dsp.generate_secret_key(profile_name, project)
|
mlrun/api/crud/hub.py
CHANGED
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
import json
|
|
16
16
|
from typing import Any, Dict, List, Optional, Tuple
|
|
17
17
|
|
|
18
|
+
import sqlalchemy.orm
|
|
19
|
+
|
|
20
|
+
import mlrun.api.utils.singletons.db
|
|
18
21
|
import mlrun.api.utils.singletons.k8s
|
|
19
22
|
import mlrun.common.schemas
|
|
20
23
|
import mlrun.common.schemas.hub
|
|
@@ -35,20 +38,6 @@ class Hub(metaclass=mlrun.utils.singleton.Singleton):
|
|
|
35
38
|
self._internal_project_name = config.hub.k8s_secrets_project_name
|
|
36
39
|
self._catalogs = {}
|
|
37
40
|
|
|
38
|
-
@staticmethod
|
|
39
|
-
def _in_k8s():
|
|
40
|
-
k8s_helper = mlrun.api.utils.singletons.k8s.get_k8s_helper()
|
|
41
|
-
return (
|
|
42
|
-
k8s_helper is not None and k8s_helper.is_running_inside_kubernetes_cluster()
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
@staticmethod
|
|
46
|
-
def _generate_credentials_secret_key(source, key=""):
|
|
47
|
-
full_key = source + secret_name_separator + key
|
|
48
|
-
return Secrets().generate_client_project_secret_key(
|
|
49
|
-
SecretsClientType.hub, full_key
|
|
50
|
-
)
|
|
51
|
-
|
|
52
41
|
def add_source(self, source: mlrun.common.schemas.hub.HubSource):
|
|
53
42
|
source_name = source.metadata.name
|
|
54
43
|
credentials = source.spec.credentials
|
|
@@ -74,118 +63,6 @@ class Hub(metaclass=mlrun.utils.singleton.Singleton):
|
|
|
74
63
|
allow_internal_secrets=True,
|
|
75
64
|
)
|
|
76
65
|
|
|
77
|
-
def _store_source_credentials(self, source_name, credentials: dict):
|
|
78
|
-
if not self._in_k8s():
|
|
79
|
-
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
80
|
-
"MLRun is not configured with k8s, hub source credentials cannot be stored securely"
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
adjusted_credentials = {
|
|
84
|
-
self._generate_credentials_secret_key(source_name, key): value
|
|
85
|
-
for key, value in credentials.items()
|
|
86
|
-
}
|
|
87
|
-
Secrets().store_project_secrets(
|
|
88
|
-
self._internal_project_name,
|
|
89
|
-
mlrun.common.schemas.SecretsData(
|
|
90
|
-
provider=mlrun.common.schemas.SecretProviderName.kubernetes,
|
|
91
|
-
secrets=adjusted_credentials,
|
|
92
|
-
),
|
|
93
|
-
allow_internal_secrets=True,
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
def _get_source_credentials(self, source_name):
|
|
97
|
-
if not self._in_k8s():
|
|
98
|
-
return {}
|
|
99
|
-
|
|
100
|
-
secret_prefix = self._generate_credentials_secret_key(source_name)
|
|
101
|
-
secrets = (
|
|
102
|
-
Secrets()
|
|
103
|
-
.list_project_secrets(
|
|
104
|
-
self._internal_project_name,
|
|
105
|
-
mlrun.common.schemas.SecretProviderName.kubernetes,
|
|
106
|
-
allow_secrets_from_k8s=True,
|
|
107
|
-
allow_internal_secrets=True,
|
|
108
|
-
)
|
|
109
|
-
.secrets
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
source_secrets = {}
|
|
113
|
-
for key, value in secrets.items():
|
|
114
|
-
if key.startswith(secret_prefix):
|
|
115
|
-
source_secrets[key[len(secret_prefix) :]] = value
|
|
116
|
-
|
|
117
|
-
return source_secrets
|
|
118
|
-
|
|
119
|
-
@staticmethod
|
|
120
|
-
def _get_asset_full_path(
|
|
121
|
-
source: mlrun.common.schemas.hub.HubSource,
|
|
122
|
-
item: mlrun.common.schemas.hub.HubItem,
|
|
123
|
-
asset: str,
|
|
124
|
-
):
|
|
125
|
-
"""
|
|
126
|
-
Combining the item path with the asset path.
|
|
127
|
-
|
|
128
|
-
:param source: Hub source object.
|
|
129
|
-
:param item: The relevant item to get the asset from.
|
|
130
|
-
:param asset: The asset name
|
|
131
|
-
:return: Full path to the asset, relative to the item directory.
|
|
132
|
-
"""
|
|
133
|
-
asset_path = item.spec.assets.get(asset, None)
|
|
134
|
-
if not asset_path:
|
|
135
|
-
raise mlrun.errors.MLRunNotFoundError(
|
|
136
|
-
f"Asset={asset} not found. "
|
|
137
|
-
f"item={item.metadata.name}, version={item.metadata.version}, tag={item.metadata.tag}"
|
|
138
|
-
)
|
|
139
|
-
item_path = item.metadata.get_relative_path()
|
|
140
|
-
return source.get_full_uri(item_path + asset_path)
|
|
141
|
-
|
|
142
|
-
@staticmethod
|
|
143
|
-
def _transform_catalog_dict_to_schema(
|
|
144
|
-
source: mlrun.common.schemas.hub.HubSource, catalog_dict: Dict[str, Any]
|
|
145
|
-
) -> mlrun.common.schemas.hub.HubCatalog:
|
|
146
|
-
"""
|
|
147
|
-
Transforms catalog dictionary to HubCatalog schema
|
|
148
|
-
:param source: Hub source object.
|
|
149
|
-
:param catalog_dict: raw catalog dict, top level keys are item names,
|
|
150
|
-
second level keys are version tags ("latest, "1.1.0", ...) and
|
|
151
|
-
bottom level keys include spec as a dict and all the rest is considered as metadata.
|
|
152
|
-
:return: catalog object
|
|
153
|
-
"""
|
|
154
|
-
catalog = mlrun.common.schemas.hub.HubCatalog(
|
|
155
|
-
catalog=[], channel=source.spec.channel
|
|
156
|
-
)
|
|
157
|
-
# Loop over objects, then over object versions.
|
|
158
|
-
for object_name, object_dict in catalog_dict.items():
|
|
159
|
-
for version_tag, version_dict in object_dict.items():
|
|
160
|
-
object_details_dict = version_dict.copy()
|
|
161
|
-
spec_dict = object_details_dict.pop("spec", {})
|
|
162
|
-
assets = object_details_dict.pop("assets", {})
|
|
163
|
-
# We want to align all item names to be normalized.
|
|
164
|
-
# This is necessary since the item names are originally collected from the yaml files
|
|
165
|
-
# which may can contain underscores.
|
|
166
|
-
object_details_dict.update(
|
|
167
|
-
{
|
|
168
|
-
"name": mlrun.utils.helpers.normalize_name(
|
|
169
|
-
object_name, verbose=False
|
|
170
|
-
)
|
|
171
|
-
}
|
|
172
|
-
)
|
|
173
|
-
metadata = mlrun.common.schemas.hub.HubItemMetadata(
|
|
174
|
-
tag=version_tag, **object_details_dict
|
|
175
|
-
)
|
|
176
|
-
item_uri = source.get_full_uri(metadata.get_relative_path())
|
|
177
|
-
spec = mlrun.common.schemas.hub.HubItemSpec(
|
|
178
|
-
item_uri=item_uri, assets=assets, **spec_dict
|
|
179
|
-
)
|
|
180
|
-
item = mlrun.common.schemas.hub.HubItem(
|
|
181
|
-
metadata=metadata,
|
|
182
|
-
spec=spec,
|
|
183
|
-
status=mlrun.common.schemas.ObjectStatus(),
|
|
184
|
-
)
|
|
185
|
-
catalog.catalog.append(item)
|
|
186
|
-
|
|
187
|
-
return catalog
|
|
188
|
-
|
|
189
66
|
def get_source_catalog(
|
|
190
67
|
self,
|
|
191
68
|
source: mlrun.common.schemas.hub.HubSource,
|
|
@@ -265,22 +142,6 @@ class Hub(metaclass=mlrun.utils.singleton.Singleton):
|
|
|
265
142
|
)
|
|
266
143
|
return items[0]
|
|
267
144
|
|
|
268
|
-
@staticmethod
|
|
269
|
-
def _get_catalog_items_filtered_by_name(
|
|
270
|
-
catalog: List[mlrun.common.schemas.hub.HubItem],
|
|
271
|
-
item_name: str,
|
|
272
|
-
) -> List[mlrun.common.schemas.hub.HubItem]:
|
|
273
|
-
"""
|
|
274
|
-
Retrieve items from catalog filtered by name
|
|
275
|
-
|
|
276
|
-
:param catalog: list of items
|
|
277
|
-
:param item_name: item name to filter by
|
|
278
|
-
|
|
279
|
-
:return: list of item objects from catalog
|
|
280
|
-
"""
|
|
281
|
-
normalized_name = mlrun.utils.helpers.normalize_name(item_name, verbose=False)
|
|
282
|
-
return [item for item in catalog if item.metadata.name == normalized_name]
|
|
283
|
-
|
|
284
145
|
def get_item_object_using_source_credentials(
|
|
285
146
|
self, source: mlrun.common.schemas.hub.HubSource, url
|
|
286
147
|
):
|
|
@@ -323,6 +184,19 @@ class Hub(metaclass=mlrun.utils.singleton.Singleton):
|
|
|
323
184
|
asset_path,
|
|
324
185
|
)
|
|
325
186
|
|
|
187
|
+
def list_hub_sources(
|
|
188
|
+
self,
|
|
189
|
+
db_session: sqlalchemy.orm.Session,
|
|
190
|
+
item_name: Optional[str] = None,
|
|
191
|
+
tag: Optional[str] = None,
|
|
192
|
+
version: Optional[str] = None,
|
|
193
|
+
) -> List[mlrun.common.schemas.IndexedHubSource]:
|
|
194
|
+
|
|
195
|
+
hub_sources = mlrun.api.utils.singletons.db.get_db().list_hub_sources(
|
|
196
|
+
db_session
|
|
197
|
+
)
|
|
198
|
+
return self.filter_hub_sources(hub_sources, item_name, tag, version)
|
|
199
|
+
|
|
326
200
|
def filter_hub_sources(
|
|
327
201
|
self,
|
|
328
202
|
sources: List[mlrun.common.schemas.IndexedHubSource],
|
|
@@ -360,3 +234,145 @@ class Hub(metaclass=mlrun.utils.singleton.Singleton):
|
|
|
360
234
|
filtered_sources.append(source)
|
|
361
235
|
break
|
|
362
236
|
return filtered_sources
|
|
237
|
+
|
|
238
|
+
@staticmethod
|
|
239
|
+
def _in_k8s():
|
|
240
|
+
k8s_helper = mlrun.api.utils.singletons.k8s.get_k8s_helper()
|
|
241
|
+
return (
|
|
242
|
+
k8s_helper is not None and k8s_helper.is_running_inside_kubernetes_cluster()
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
@staticmethod
|
|
246
|
+
def _generate_credentials_secret_key(source, key=""):
|
|
247
|
+
full_key = source + secret_name_separator + key
|
|
248
|
+
return Secrets().generate_client_project_secret_key(
|
|
249
|
+
SecretsClientType.hub, full_key
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
def _store_source_credentials(self, source_name, credentials: dict):
|
|
253
|
+
if not self._in_k8s():
|
|
254
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
255
|
+
"MLRun is not configured with k8s, hub source credentials cannot be stored securely"
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
adjusted_credentials = {
|
|
259
|
+
self._generate_credentials_secret_key(source_name, key): value
|
|
260
|
+
for key, value in credentials.items()
|
|
261
|
+
}
|
|
262
|
+
Secrets().store_project_secrets(
|
|
263
|
+
self._internal_project_name,
|
|
264
|
+
mlrun.common.schemas.SecretsData(
|
|
265
|
+
provider=mlrun.common.schemas.SecretProviderName.kubernetes,
|
|
266
|
+
secrets=adjusted_credentials,
|
|
267
|
+
),
|
|
268
|
+
allow_internal_secrets=True,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
def _get_source_credentials(self, source_name):
|
|
272
|
+
if not self._in_k8s():
|
|
273
|
+
return {}
|
|
274
|
+
|
|
275
|
+
secret_prefix = self._generate_credentials_secret_key(source_name)
|
|
276
|
+
secrets = (
|
|
277
|
+
Secrets()
|
|
278
|
+
.list_project_secrets(
|
|
279
|
+
self._internal_project_name,
|
|
280
|
+
mlrun.common.schemas.SecretProviderName.kubernetes,
|
|
281
|
+
allow_secrets_from_k8s=True,
|
|
282
|
+
allow_internal_secrets=True,
|
|
283
|
+
)
|
|
284
|
+
.secrets
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
source_secrets = {}
|
|
288
|
+
for key, value in secrets.items():
|
|
289
|
+
if key.startswith(secret_prefix):
|
|
290
|
+
source_secrets[key[len(secret_prefix) :]] = value
|
|
291
|
+
|
|
292
|
+
return source_secrets
|
|
293
|
+
|
|
294
|
+
@staticmethod
|
|
295
|
+
def _get_asset_full_path(
|
|
296
|
+
source: mlrun.common.schemas.hub.HubSource,
|
|
297
|
+
item: mlrun.common.schemas.hub.HubItem,
|
|
298
|
+
asset: str,
|
|
299
|
+
):
|
|
300
|
+
"""
|
|
301
|
+
Combining the item path with the asset path.
|
|
302
|
+
|
|
303
|
+
:param source: Hub source object.
|
|
304
|
+
:param item: The relevant item to get the asset from.
|
|
305
|
+
:param asset: The asset name
|
|
306
|
+
:return: Full path to the asset, relative to the item directory.
|
|
307
|
+
"""
|
|
308
|
+
asset_path = item.spec.assets.get(asset, None)
|
|
309
|
+
if not asset_path:
|
|
310
|
+
raise mlrun.errors.MLRunNotFoundError(
|
|
311
|
+
f"Asset={asset} not found. "
|
|
312
|
+
f"item={item.metadata.name}, version={item.metadata.version}, tag={item.metadata.tag}"
|
|
313
|
+
)
|
|
314
|
+
item_path = item.metadata.get_relative_path()
|
|
315
|
+
return source.get_full_uri(item_path + asset_path)
|
|
316
|
+
|
|
317
|
+
@staticmethod
|
|
318
|
+
def _transform_catalog_dict_to_schema(
|
|
319
|
+
source: mlrun.common.schemas.hub.HubSource, catalog_dict: Dict[str, Any]
|
|
320
|
+
) -> mlrun.common.schemas.hub.HubCatalog:
|
|
321
|
+
"""
|
|
322
|
+
Transforms catalog dictionary to HubCatalog schema
|
|
323
|
+
:param source: Hub source object.
|
|
324
|
+
:param catalog_dict: raw catalog dict, top level keys are item names,
|
|
325
|
+
second level keys are version tags ("latest, "1.1.0", ...) and
|
|
326
|
+
bottom level keys include spec as a dict and all the rest is considered as metadata.
|
|
327
|
+
:return: catalog object
|
|
328
|
+
"""
|
|
329
|
+
catalog = mlrun.common.schemas.hub.HubCatalog(
|
|
330
|
+
catalog=[], channel=source.spec.channel
|
|
331
|
+
)
|
|
332
|
+
# Loop over objects, then over object versions.
|
|
333
|
+
for object_name, object_dict in catalog_dict.items():
|
|
334
|
+
for version_tag, version_dict in object_dict.items():
|
|
335
|
+
object_details_dict = version_dict.copy()
|
|
336
|
+
spec_dict = object_details_dict.pop("spec", {})
|
|
337
|
+
assets = object_details_dict.pop("assets", {})
|
|
338
|
+
# We want to align all item names to be normalized.
|
|
339
|
+
# This is necessary since the item names are originally collected from the yaml files
|
|
340
|
+
# which may can contain underscores.
|
|
341
|
+
object_details_dict.update(
|
|
342
|
+
{
|
|
343
|
+
"name": mlrun.utils.helpers.normalize_name(
|
|
344
|
+
object_name, verbose=False
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
)
|
|
348
|
+
metadata = mlrun.common.schemas.hub.HubItemMetadata(
|
|
349
|
+
tag=version_tag, **object_details_dict
|
|
350
|
+
)
|
|
351
|
+
item_uri = source.get_full_uri(metadata.get_relative_path())
|
|
352
|
+
spec = mlrun.common.schemas.hub.HubItemSpec(
|
|
353
|
+
item_uri=item_uri, assets=assets, **spec_dict
|
|
354
|
+
)
|
|
355
|
+
item = mlrun.common.schemas.hub.HubItem(
|
|
356
|
+
metadata=metadata,
|
|
357
|
+
spec=spec,
|
|
358
|
+
status=mlrun.common.schemas.ObjectStatus(),
|
|
359
|
+
)
|
|
360
|
+
catalog.catalog.append(item)
|
|
361
|
+
|
|
362
|
+
return catalog
|
|
363
|
+
|
|
364
|
+
@staticmethod
|
|
365
|
+
def _get_catalog_items_filtered_by_name(
|
|
366
|
+
catalog: List[mlrun.common.schemas.hub.HubItem],
|
|
367
|
+
item_name: str,
|
|
368
|
+
) -> List[mlrun.common.schemas.hub.HubItem]:
|
|
369
|
+
"""
|
|
370
|
+
Retrieve items from catalog filtered by name
|
|
371
|
+
|
|
372
|
+
:param catalog: list of items
|
|
373
|
+
:param item_name: item name to filter by
|
|
374
|
+
|
|
375
|
+
:return: list of item objects from catalog
|
|
376
|
+
"""
|
|
377
|
+
normalized_name = mlrun.utils.helpers.normalize_name(item_name, verbose=False)
|
|
378
|
+
return [item for item in catalog if item.metadata.name == normalized_name]
|
mlrun/api/crud/workflows.py
CHANGED
|
@@ -26,6 +26,7 @@ from mlrun.api.api.utils import (
|
|
|
26
26
|
)
|
|
27
27
|
from mlrun.config import config
|
|
28
28
|
from mlrun.model import Credentials, RunMetadata, RunObject, RunSpec
|
|
29
|
+
from mlrun.utils import fill_project_path_template
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
class WorkflowRunners(
|
|
@@ -161,9 +162,12 @@ class WorkflowRunners(
|
|
|
161
162
|
),
|
|
162
163
|
handler="mlrun.projects.load_and_run",
|
|
163
164
|
scrape_metrics=config.scrape_metrics,
|
|
164
|
-
output_path=(
|
|
165
|
-
workflow_request.artifact_path or config.artifact_path
|
|
166
|
-
|
|
165
|
+
output_path=fill_project_path_template(
|
|
166
|
+
(workflow_request.artifact_path or config.artifact_path).replace(
|
|
167
|
+
"{{run.uid}}", meta_uid
|
|
168
|
+
),
|
|
169
|
+
project.metadata.name,
|
|
170
|
+
),
|
|
167
171
|
),
|
|
168
172
|
metadata=RunMetadata(
|
|
169
173
|
uid=meta_uid, name=workflow_spec.name, project=project.metadata.name
|