qwak-core 0.4.272__py3-none-any.whl → 0.4.274__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.
- frogml_storage/__init__.py +1 -0
- frogml_storage/artifactory/__init__.py +1 -0
- frogml_storage/artifactory/_artifactory_api.py +315 -0
- frogml_storage/authentication/login/__init__.py +1 -0
- frogml_storage/authentication/login/_login_cli.py +239 -0
- frogml_storage/authentication/login/_login_command.py +74 -0
- frogml_storage/authentication/models/__init__.py +3 -0
- frogml_storage/authentication/models/_auth.py +24 -0
- frogml_storage/authentication/models/_auth_config.py +70 -0
- frogml_storage/authentication/models/_login.py +22 -0
- frogml_storage/authentication/utils/__init__.py +17 -0
- frogml_storage/authentication/utils/_authentication_utils.py +281 -0
- frogml_storage/authentication/utils/_login_checks_utils.py +114 -0
- frogml_storage/base_storage.py +140 -0
- frogml_storage/constants.py +56 -0
- frogml_storage/exceptions/checksum_verification_error.py +3 -0
- frogml_storage/exceptions/validation_error.py +4 -0
- frogml_storage/frog_ml.py +668 -0
- frogml_storage/http/__init__.py +1 -0
- frogml_storage/http/http_client.py +83 -0
- frogml_storage/logging/__init__.py +1 -0
- frogml_storage/logging/_log_config.py +45 -0
- frogml_storage/logging/log_utils.py +21 -0
- frogml_storage/models/__init__.py +1 -0
- frogml_storage/models/_download_context.py +54 -0
- frogml_storage/models/dataset_manifest.py +13 -0
- frogml_storage/models/entity_manifest.py +93 -0
- frogml_storage/models/frogml_dataset_version.py +21 -0
- frogml_storage/models/frogml_entity_type_info.py +50 -0
- frogml_storage/models/frogml_entity_version.py +34 -0
- frogml_storage/models/frogml_model_version.py +21 -0
- frogml_storage/models/model_manifest.py +60 -0
- frogml_storage/models/serialization_metadata.py +15 -0
- frogml_storage/utils/__init__.py +12 -0
- frogml_storage/utils/_environment.py +21 -0
- frogml_storage/utils/_input_checks_utility.py +104 -0
- frogml_storage/utils/_storage_utils.py +15 -0
- frogml_storage/utils/_url_utils.py +27 -0
- qwak/__init__.py +1 -1
- qwak/clients/batch_job_management/client.py +39 -0
- qwak/clients/instance_template/client.py +6 -4
- qwak/clients/prompt_manager/model_descriptor_mapper.py +21 -19
- qwak/feature_store/_common/artifact_utils.py +3 -3
- qwak/feature_store/data_sources/base.py +4 -4
- qwak/feature_store/data_sources/batch/athena.py +3 -3
- qwak/feature_store/feature_sets/streaming.py +3 -3
- qwak/feature_store/feature_sets/streaming_backfill.py +1 -1
- qwak/feature_store/online/client.py +6 -6
- qwak/feature_store/sinks/streaming/factory.py +1 -1
- qwak/inner/build_logic/phases/phase_010_fetch_model/fetch_strategy_manager/strategy/git/git_strategy.py +3 -3
- qwak/inner/di_configuration/account.py +23 -24
- qwak/inner/tool/auth.py +2 -2
- qwak/llmops/provider/openai/provider.py +3 -3
- qwak/model/tools/adapters/output.py +1 -1
- qwak/model/utils/feature_utils.py +12 -8
- qwak/model_loggers/artifact_logger.py +7 -7
- qwak/tools/logger/logger.py +1 -1
- qwak_core-0.4.274.dist-info/METADATA +415 -0
- {qwak_core-0.4.272.dist-info → qwak_core-0.4.274.dist-info}/RECORD +61 -25
- qwak_services_mock/mocks/batch_job_manager_service.py +24 -1
- _qwak_proto/__init__.py +0 -0
- _qwak_proto/qwak/__init__.py +0 -0
- qwak_core-0.4.272.dist-info/METADATA +0 -53
- {qwak_core-0.4.272.dist-info → qwak_core-0.4.274.dist-info}/WHEEL +0 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from frogml_storage.constants import (
|
4
|
+
FROG_ML_CONFIG_ACCESS_TOKEN,
|
5
|
+
FROG_ML_CONFIG_ARTIFACTORY_URL,
|
6
|
+
FROG_ML_CONFIG_PASSWORD,
|
7
|
+
FROG_ML_CONFIG_USER,
|
8
|
+
SERVER_ID,
|
9
|
+
)
|
10
|
+
|
11
|
+
|
12
|
+
class AuthConfig(object):
|
13
|
+
def __init__(
|
14
|
+
self,
|
15
|
+
artifactory_url: Optional[str] = None,
|
16
|
+
access_token: Optional[str] = None,
|
17
|
+
user: Optional[str] = None,
|
18
|
+
password: Optional[str] = None,
|
19
|
+
server_id: Optional[str] = None,
|
20
|
+
) -> None:
|
21
|
+
self.artifactory_url = artifactory_url
|
22
|
+
self.user = user
|
23
|
+
self.password = password
|
24
|
+
self.access_token = access_token
|
25
|
+
self.server_id = server_id
|
26
|
+
|
27
|
+
def to_json(self):
|
28
|
+
include_keys = [
|
29
|
+
FROG_ML_CONFIG_ARTIFACTORY_URL,
|
30
|
+
FROG_ML_CONFIG_USER,
|
31
|
+
FROG_ML_CONFIG_PASSWORD,
|
32
|
+
FROG_ML_CONFIG_ACCESS_TOKEN,
|
33
|
+
SERVER_ID,
|
34
|
+
]
|
35
|
+
return {
|
36
|
+
key: getattr(self, key)
|
37
|
+
for key in include_keys
|
38
|
+
if getattr(self, key) is not None
|
39
|
+
}
|
40
|
+
|
41
|
+
@classmethod
|
42
|
+
def by_access_token(cls, artifactory_url: str, access_token: str) -> "AuthConfig":
|
43
|
+
return cls(
|
44
|
+
artifactory_url=artifactory_url,
|
45
|
+
access_token=access_token,
|
46
|
+
)
|
47
|
+
|
48
|
+
@classmethod
|
49
|
+
def by_basic_auth(
|
50
|
+
cls, artifactory_url: str, user: str, password: str
|
51
|
+
) -> "AuthConfig":
|
52
|
+
return cls(
|
53
|
+
artifactory_url=artifactory_url,
|
54
|
+
user=user,
|
55
|
+
password=password,
|
56
|
+
)
|
57
|
+
|
58
|
+
@classmethod
|
59
|
+
def by_server_id(cls, server_id: str) -> "AuthConfig":
|
60
|
+
return cls(server_id=server_id)
|
61
|
+
|
62
|
+
@classmethod
|
63
|
+
def from_dict(cls, data: dict) -> "AuthConfig":
|
64
|
+
return cls(
|
65
|
+
artifactory_url=data.get(FROG_ML_CONFIG_ARTIFACTORY_URL),
|
66
|
+
access_token=data.get(FROG_ML_CONFIG_ACCESS_TOKEN),
|
67
|
+
server_id=data.get(SERVER_ID),
|
68
|
+
user=data.get(FROG_ML_CONFIG_USER),
|
69
|
+
password=data.get(FROG_ML_CONFIG_PASSWORD),
|
70
|
+
)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
|
4
|
+
class LoginArguments(object):
|
5
|
+
isAnonymous: bool = False
|
6
|
+
server_id: Optional[str] = None
|
7
|
+
username: Optional[str] = None
|
8
|
+
password: Optional[str] = None
|
9
|
+
access_token: Optional[str] = None
|
10
|
+
artifactory_url: Optional[str] = None
|
11
|
+
|
12
|
+
def __eq__(self, other):
|
13
|
+
if not isinstance(other, LoginArguments):
|
14
|
+
return False
|
15
|
+
return (
|
16
|
+
self.isAnonymous == other.isAnonymous
|
17
|
+
and self.server_id == other.server_id
|
18
|
+
and self.username == other.username
|
19
|
+
and self.password == other.password
|
20
|
+
and self.access_token == other.access_token
|
21
|
+
and self.artifactory_url == other.artifactory_url
|
22
|
+
)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from ._authentication_utils import (
|
2
|
+
get_credentials,
|
3
|
+
get_frogml_configuration,
|
4
|
+
parse_cli_config_server,
|
5
|
+
read_jfrog_cli_config,
|
6
|
+
get_encrypted_password,
|
7
|
+
save_auth_config,
|
8
|
+
get_list_of_servers_from_config,
|
9
|
+
read_frogml_config,
|
10
|
+
)
|
11
|
+
from ._login_checks_utils import (
|
12
|
+
login_input_checks,
|
13
|
+
is_login_without_params,
|
14
|
+
is_username_password_login,
|
15
|
+
is_access_token_login,
|
16
|
+
is_anonymous_login,
|
17
|
+
)
|
@@ -0,0 +1,281 @@
|
|
1
|
+
import http
|
2
|
+
import json
|
3
|
+
import os
|
4
|
+
from typing import Optional, Tuple, Union, List
|
5
|
+
|
6
|
+
import requests
|
7
|
+
from requests.auth import AuthBase, HTTPBasicAuth
|
8
|
+
|
9
|
+
from frogml_storage.utils import join_url
|
10
|
+
from frogml_storage.artifactory import ArtifactoryApi
|
11
|
+
from frogml_storage.authentication.models import (
|
12
|
+
BearerAuth,
|
13
|
+
EmptyAuth,
|
14
|
+
AuthConfig,
|
15
|
+
LoginArguments,
|
16
|
+
)
|
17
|
+
from frogml_storage.constants import (
|
18
|
+
CONFIG_FILE_PATH,
|
19
|
+
FROG_ML_CONFIG_ACCESS_TOKEN,
|
20
|
+
FROG_ML_CONFIG_ARTIFACTORY_URL,
|
21
|
+
FROG_ML_CONFIG_PASSWORD,
|
22
|
+
FROG_ML_CONFIG_USER,
|
23
|
+
JF_ACCESS_TOKEN,
|
24
|
+
JF_URL,
|
25
|
+
JFROG_CLI_CONFIG_ACCESS_TOKEN,
|
26
|
+
JFROG_CLI_CONFIG_ARTIFACTORY_URL,
|
27
|
+
JFROG_CLI_CONFIG_FILE_PATH,
|
28
|
+
JFROG_CLI_CONFIG_PASSWORD,
|
29
|
+
JFROG_CLI_CONFIG_URL,
|
30
|
+
JFROG_CLI_CONFIG_USER,
|
31
|
+
SERVER_ID,
|
32
|
+
)
|
33
|
+
from frogml_storage.logging import logger
|
34
|
+
|
35
|
+
|
36
|
+
def read_jfrog_cli_config() -> Union[dict, None]:
|
37
|
+
try:
|
38
|
+
with open(JFROG_CLI_CONFIG_FILE_PATH, "r") as file:
|
39
|
+
config_data = json.load(file)
|
40
|
+
return config_data
|
41
|
+
except FileNotFoundError:
|
42
|
+
logger.debug("JFrog cli config file was not found.")
|
43
|
+
return None
|
44
|
+
except json.JSONDecodeError as e:
|
45
|
+
print(f"JFrog cli config file is not a valid JSON {e}.")
|
46
|
+
return None
|
47
|
+
|
48
|
+
|
49
|
+
def read_frogml_config() -> Union[dict, None]:
|
50
|
+
try:
|
51
|
+
with open(CONFIG_FILE_PATH, "r") as file:
|
52
|
+
config_data = json.load(file)
|
53
|
+
return config_data
|
54
|
+
except FileNotFoundError:
|
55
|
+
logger.debug("FrogMl config file was not found.")
|
56
|
+
return None
|
57
|
+
except json.JSONDecodeError as e:
|
58
|
+
print(f"FrogMl config file is not a valid JSON {e}.")
|
59
|
+
return None
|
60
|
+
|
61
|
+
|
62
|
+
def parse_cli_config_server(server_config: dict) -> Union[LoginArguments, None]:
|
63
|
+
login_args = LoginArguments()
|
64
|
+
login_args.server_id = server_config.get(SERVER_ID)
|
65
|
+
|
66
|
+
if JFROG_CLI_CONFIG_ARTIFACTORY_URL in server_config:
|
67
|
+
login_args.artifactory_url = server_config.get(JFROG_CLI_CONFIG_ARTIFACTORY_URL)
|
68
|
+
elif (
|
69
|
+
JFROG_CLI_CONFIG_URL in server_config
|
70
|
+
and server_config.get(JFROG_CLI_CONFIG_URL) is not None
|
71
|
+
):
|
72
|
+
login_args.artifactory_url = join_url(
|
73
|
+
server_config[JFROG_CLI_CONFIG_URL], "artifactory"
|
74
|
+
)
|
75
|
+
else:
|
76
|
+
logger.debug(
|
77
|
+
"Invalid JFrog CLI file, expected either artifactoryUrl or url in jfrog cli config file"
|
78
|
+
)
|
79
|
+
return None
|
80
|
+
|
81
|
+
if JFROG_CLI_CONFIG_ACCESS_TOKEN in server_config:
|
82
|
+
login_args.access_token = server_config.get(JFROG_CLI_CONFIG_ACCESS_TOKEN)
|
83
|
+
elif (
|
84
|
+
JFROG_CLI_CONFIG_USER in server_config
|
85
|
+
and JFROG_CLI_CONFIG_PASSWORD in server_config
|
86
|
+
):
|
87
|
+
login_args.username = server_config.get(JFROG_CLI_CONFIG_USER)
|
88
|
+
login_args.password = server_config.get(JFROG_CLI_CONFIG_PASSWORD)
|
89
|
+
else:
|
90
|
+
logger.debug(
|
91
|
+
"Expected either accessToken or user/password in jfrog cli config file"
|
92
|
+
)
|
93
|
+
return None
|
94
|
+
|
95
|
+
return login_args
|
96
|
+
|
97
|
+
|
98
|
+
def get_frogml_configuration() -> Union[LoginArguments, None]:
|
99
|
+
frog_ml_config = read_frogml_config()
|
100
|
+
if (
|
101
|
+
frog_ml_config is not None
|
102
|
+
and frog_ml_config.get("servers") is not None
|
103
|
+
and len(frog_ml_config["servers"]) > 0
|
104
|
+
):
|
105
|
+
server_config = frog_ml_config["servers"][0]
|
106
|
+
login_args = LoginArguments()
|
107
|
+
if FROG_ML_CONFIG_ARTIFACTORY_URL in server_config:
|
108
|
+
login_args.artifactory_url = server_config.get(
|
109
|
+
FROG_ML_CONFIG_ARTIFACTORY_URL
|
110
|
+
)
|
111
|
+
else:
|
112
|
+
logger.debug(
|
113
|
+
"Invalid FrogMl authentication file, expected either artifactory_url in FrogMl authentication file"
|
114
|
+
)
|
115
|
+
return None
|
116
|
+
|
117
|
+
if FROG_ML_CONFIG_ACCESS_TOKEN in server_config:
|
118
|
+
login_args.access_token = server_config.get(FROG_ML_CONFIG_ACCESS_TOKEN)
|
119
|
+
elif (
|
120
|
+
FROG_ML_CONFIG_USER in server_config
|
121
|
+
and FROG_ML_CONFIG_PASSWORD in server_config
|
122
|
+
):
|
123
|
+
login_args.username = server_config.get(FROG_ML_CONFIG_USER)
|
124
|
+
login_args.password = server_config.get(FROG_ML_CONFIG_PASSWORD)
|
125
|
+
elif (
|
126
|
+
FROG_ML_CONFIG_USER in server_config
|
127
|
+
and FROG_ML_CONFIG_PASSWORD not in server_config
|
128
|
+
or (
|
129
|
+
FROG_ML_CONFIG_USER not in server_config
|
130
|
+
and FROG_ML_CONFIG_PASSWORD in server_config
|
131
|
+
)
|
132
|
+
):
|
133
|
+
logger.debug(
|
134
|
+
"Invalid FrogMl authentication file, username or password is missing in FrogMl authentication file"
|
135
|
+
)
|
136
|
+
return None
|
137
|
+
elif (
|
138
|
+
login_args.username is None
|
139
|
+
and login_args.password is None
|
140
|
+
and login_args.access_token is None
|
141
|
+
):
|
142
|
+
login_args.isAnonymous = True
|
143
|
+
return login_args
|
144
|
+
else:
|
145
|
+
return None
|
146
|
+
|
147
|
+
|
148
|
+
def get_encrypted_password(
|
149
|
+
auth_config: AuthConfig, auth_token: AuthBase
|
150
|
+
) -> Optional[str]:
|
151
|
+
try:
|
152
|
+
response = ArtifactoryApi(
|
153
|
+
auth_config.artifactory_url, auth_token
|
154
|
+
).encrypt_password()
|
155
|
+
if response.status_code != http.HTTPStatus.OK:
|
156
|
+
logger.debug(
|
157
|
+
f"Expected {http.HTTPStatus.OK} status but got {response.status_code} "
|
158
|
+
f"when using url {auth_config.artifactory_url}"
|
159
|
+
)
|
160
|
+
print("Error while trying to encrypt password.")
|
161
|
+
return None
|
162
|
+
return response.text
|
163
|
+
except requests.exceptions.RequestException as e:
|
164
|
+
print(f"Error while trying to encrypt password: {e}.")
|
165
|
+
return None
|
166
|
+
|
167
|
+
|
168
|
+
def save_auth_config(auth_config: AuthConfig) -> None:
|
169
|
+
file_content: dict[str, list] = {}
|
170
|
+
file_content.setdefault("servers", []).append(auth_config.to_json())
|
171
|
+
os.makedirs(os.path.dirname(CONFIG_FILE_PATH), exist_ok=True)
|
172
|
+
with open(CONFIG_FILE_PATH, "w") as file:
|
173
|
+
json.dump(file_content, file, indent=2)
|
174
|
+
|
175
|
+
|
176
|
+
def get_credentials(auth_config: Optional[AuthConfig] = None) -> Tuple[str, AuthBase]:
|
177
|
+
if not __should_use_file_auth(auth_config):
|
178
|
+
__validate_credentials(auth_config)
|
179
|
+
return __auth_config_to_auth_tuple(auth_config)
|
180
|
+
logger.debug(
|
181
|
+
"Login configuration not supplied, attempting to find environment variables"
|
182
|
+
)
|
183
|
+
|
184
|
+
if __should_use_environment_variables():
|
185
|
+
return get_environment_variables()
|
186
|
+
|
187
|
+
logger.debug(
|
188
|
+
"Environment variables not supplied, attempting to load configuration from file"
|
189
|
+
)
|
190
|
+
|
191
|
+
if os.path.exists(CONFIG_FILE_PATH):
|
192
|
+
return __read_credentials_from_file(CONFIG_FILE_PATH)
|
193
|
+
raise ValueError(
|
194
|
+
f"Configuration were not provided and configuration file not found in {CONFIG_FILE_PATH},"
|
195
|
+
f" either pass configuration in the constructor, add env variables or create the configuration file by "
|
196
|
+
f"running `frogml login`"
|
197
|
+
)
|
198
|
+
|
199
|
+
|
200
|
+
def __should_use_environment_variables() -> bool:
|
201
|
+
return os.getenv("JF_URL") is not None
|
202
|
+
|
203
|
+
|
204
|
+
def get_environment_variables() -> Tuple[str, AuthBase]:
|
205
|
+
auth_config: AuthConfig = AuthConfig(
|
206
|
+
artifactory_url=os.getenv(JF_URL),
|
207
|
+
access_token=os.getenv(JF_ACCESS_TOKEN),
|
208
|
+
)
|
209
|
+
|
210
|
+
return __auth_config_to_auth_tuple(auth_config)
|
211
|
+
|
212
|
+
|
213
|
+
def __should_use_file_auth(credentials: Optional[AuthConfig] = None) -> bool:
|
214
|
+
return credentials is None or (
|
215
|
+
credentials.artifactory_url is None
|
216
|
+
and credentials.user is None
|
217
|
+
and credentials.password is None
|
218
|
+
and credentials.access_token is None
|
219
|
+
)
|
220
|
+
|
221
|
+
|
222
|
+
def __validate_credentials(credentials: Optional[AuthConfig]) -> None:
|
223
|
+
if credentials is None:
|
224
|
+
raise ValueError("Credentials must be provided.")
|
225
|
+
if credentials.artifactory_url is None:
|
226
|
+
raise ValueError("Credentials must contain artifactory url.")
|
227
|
+
return None
|
228
|
+
|
229
|
+
|
230
|
+
def __read_credentials_from_file(file_path: str) -> Tuple[str, AuthBase]:
|
231
|
+
try:
|
232
|
+
with open(file_path, "r") as file:
|
233
|
+
config_content: dict = json.load(file)
|
234
|
+
servers = config_content.get("servers")
|
235
|
+
if servers is None or len(servers) == 0:
|
236
|
+
raise ValueError(
|
237
|
+
"Configuration file was found but it's empty, failing authentication"
|
238
|
+
)
|
239
|
+
server = servers[0]
|
240
|
+
return __auth_config_to_auth_tuple(AuthConfig.from_dict(server))
|
241
|
+
except json.JSONDecodeError:
|
242
|
+
raise ValueError(f"Error when reading {file_path}, please recreate the file.")
|
243
|
+
|
244
|
+
|
245
|
+
def __auth_config_to_auth_tuple(
|
246
|
+
auth_config: Optional[AuthConfig],
|
247
|
+
) -> Tuple[str, AuthBase]:
|
248
|
+
|
249
|
+
if auth_config is None:
|
250
|
+
raise ValueError("No authentication configuration provided")
|
251
|
+
|
252
|
+
auth: AuthBase = EmptyAuth()
|
253
|
+
if auth_config.access_token is not None:
|
254
|
+
auth = BearerAuth(auth_config.access_token)
|
255
|
+
elif auth_config.user is not None and auth_config.password is not None:
|
256
|
+
auth = HTTPBasicAuth(auth_config.user, auth_config.password)
|
257
|
+
elif auth_config.user is not None or auth_config.password is not None:
|
258
|
+
raise ValueError("User and password must be provided together")
|
259
|
+
|
260
|
+
if auth_config.artifactory_url is None:
|
261
|
+
raise ValueError("No artifactory URL provided")
|
262
|
+
|
263
|
+
return auth_config.artifactory_url, auth
|
264
|
+
|
265
|
+
|
266
|
+
def get_list_of_servers_from_config(jfrog_cli_config: dict) -> List[str]:
|
267
|
+
if jfrog_cli_config is not None:
|
268
|
+
servers = jfrog_cli_config.get("servers")
|
269
|
+
if servers is not None:
|
270
|
+
return list(map(__map_server_ids, servers))
|
271
|
+
|
272
|
+
return []
|
273
|
+
|
274
|
+
|
275
|
+
def __map_server_ids(server: dict) -> str:
|
276
|
+
server_id = ""
|
277
|
+
if server is not None:
|
278
|
+
server_id = str(server.get("serverId"))
|
279
|
+
if server.get("isDefault") is not None and bool(server.get("isDefault")):
|
280
|
+
server_id = server_id + " (Default)"
|
281
|
+
return server_id
|
@@ -0,0 +1,114 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from frogml_storage.authentication.models import AuthConfig
|
4
|
+
|
5
|
+
|
6
|
+
def login_input_checks(
|
7
|
+
url: Optional[str] = None,
|
8
|
+
username: Optional[str] = None,
|
9
|
+
password: Optional[str] = None,
|
10
|
+
token: Optional[str] = None,
|
11
|
+
anonymous: bool = False,
|
12
|
+
) -> bool:
|
13
|
+
return (
|
14
|
+
__is_user_name_password_command(url, username, password, token, anonymous)
|
15
|
+
or __is_access_token_command(url, username, password, token, anonymous)
|
16
|
+
or __is_anonymous_command(url, username, password, token, anonymous)
|
17
|
+
)
|
18
|
+
|
19
|
+
|
20
|
+
def is_login_without_params(
|
21
|
+
url: Optional[str] = None,
|
22
|
+
username: Optional[str] = None,
|
23
|
+
password: Optional[str] = None,
|
24
|
+
token: Optional[str] = None,
|
25
|
+
anonymous: bool = False,
|
26
|
+
) -> bool:
|
27
|
+
return (
|
28
|
+
all(params is None for params in [url, username, password, token])
|
29
|
+
and not anonymous
|
30
|
+
)
|
31
|
+
|
32
|
+
|
33
|
+
def is_username_password_login(
|
34
|
+
auth_config: AuthConfig, anonymous: Optional[bool] = False
|
35
|
+
) -> bool:
|
36
|
+
return __is_user_name_password_command(
|
37
|
+
url=auth_config.artifactory_url,
|
38
|
+
username=auth_config.user,
|
39
|
+
password=auth_config.password,
|
40
|
+
token=auth_config.access_token,
|
41
|
+
anonymous=anonymous,
|
42
|
+
)
|
43
|
+
|
44
|
+
|
45
|
+
def is_access_token_login(
|
46
|
+
auth_config: AuthConfig, anonymous: Optional[bool] = False
|
47
|
+
) -> bool:
|
48
|
+
if anonymous is None:
|
49
|
+
anonymous = False
|
50
|
+
return __is_access_token_command(
|
51
|
+
url=auth_config.artifactory_url,
|
52
|
+
username=auth_config.user,
|
53
|
+
password=auth_config.password,
|
54
|
+
token=auth_config.access_token,
|
55
|
+
anonymous=anonymous,
|
56
|
+
)
|
57
|
+
|
58
|
+
|
59
|
+
def is_anonymous_login(
|
60
|
+
auth_config: AuthConfig, anonymous: Optional[bool] = False
|
61
|
+
) -> bool:
|
62
|
+
if anonymous is None:
|
63
|
+
anonymous = False
|
64
|
+
return __is_anonymous_command(
|
65
|
+
url=auth_config.artifactory_url,
|
66
|
+
username=auth_config.user,
|
67
|
+
password=auth_config.password,
|
68
|
+
token=auth_config.access_token,
|
69
|
+
anonymous=anonymous,
|
70
|
+
)
|
71
|
+
|
72
|
+
|
73
|
+
def __is_anonymous_command(
|
74
|
+
url: Optional[str],
|
75
|
+
username: Optional[str],
|
76
|
+
password: Optional[str],
|
77
|
+
token: Optional[str],
|
78
|
+
anonymous: bool,
|
79
|
+
) -> bool:
|
80
|
+
return (
|
81
|
+
anonymous
|
82
|
+
and all(params is None for params in [username, password, token])
|
83
|
+
and url is not None
|
84
|
+
)
|
85
|
+
|
86
|
+
|
87
|
+
def __is_access_token_command(
|
88
|
+
url: Optional[str],
|
89
|
+
username: Optional[str],
|
90
|
+
password: Optional[str],
|
91
|
+
token: Optional[str],
|
92
|
+
anonymous: bool,
|
93
|
+
) -> bool:
|
94
|
+
if anonymous:
|
95
|
+
return False
|
96
|
+
return all(params is None for params in [username, password]) and all(
|
97
|
+
params is not None for params in [url, token]
|
98
|
+
)
|
99
|
+
|
100
|
+
|
101
|
+
def __is_user_name_password_command(
|
102
|
+
url: Optional[str],
|
103
|
+
username: Optional[str],
|
104
|
+
password: Optional[str],
|
105
|
+
token: Optional[str],
|
106
|
+
anonymous: Optional[bool] = False,
|
107
|
+
) -> bool:
|
108
|
+
return (
|
109
|
+
not anonymous
|
110
|
+
and url is not None
|
111
|
+
and username is not None
|
112
|
+
and password is not None
|
113
|
+
and token is None
|
114
|
+
)
|
@@ -0,0 +1,140 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import Dict, List, Optional, Union
|
3
|
+
|
4
|
+
from frogml_storage.models.frogml_dataset_version import FrogMLDatasetVersion
|
5
|
+
from frogml_storage.models.frogml_model_version import FrogMLModelVersion
|
6
|
+
from frogml_storage.models.serialization_metadata import SerializationMetadata
|
7
|
+
|
8
|
+
|
9
|
+
class BaseStorage(ABC):
|
10
|
+
"""
|
11
|
+
Repository storage to download or store model | dataset artifacts,
|
12
|
+
metrics and relation between these in an Artifactory repository.
|
13
|
+
"""
|
14
|
+
|
15
|
+
@abstractmethod
|
16
|
+
def upload_model_version(
|
17
|
+
self,
|
18
|
+
repository: str,
|
19
|
+
model_name: str,
|
20
|
+
model_path: str,
|
21
|
+
model_type: Union[SerializationMetadata, Dict],
|
22
|
+
namespace: Optional[str] = None,
|
23
|
+
version: Optional[str] = None,
|
24
|
+
properties: Optional[dict[str, str]] = None,
|
25
|
+
dependencies_files_paths: Optional[List[str]] = None,
|
26
|
+
code_archive_file_path: Optional[str] = None,
|
27
|
+
) -> FrogMLModelVersion:
|
28
|
+
"""Upload model to a repository in Artifactory. Uploaded models should be stored with the following layout:
|
29
|
+
├── REPO
|
30
|
+
├── models
|
31
|
+
├── ${NAMESPACE}
|
32
|
+
├── ${MODEL_NAME}
|
33
|
+
├── ${MODEL_VERSION}
|
34
|
+
├── model-manifest.json
|
35
|
+
├── model
|
36
|
+
├── model.pkl
|
37
|
+
├── evidence.json
|
38
|
+
├── ...
|
39
|
+
├── code
|
40
|
+
├── code.zip
|
41
|
+
├── requirements.txt
|
42
|
+
:param repository: the repository to upload the model to
|
43
|
+
:param model_name: the name of the model
|
44
|
+
:param model_path: the source path of the model
|
45
|
+
:param model_type: the type of the model (PyTorch, HuggingFace, Catboost)
|
46
|
+
:param namespace: the namespace to upload the model to
|
47
|
+
:param version: the version of the model
|
48
|
+
:param properties: tags to associate with the model
|
49
|
+
:param dependencies_files_paths: the list of dependencies files paths
|
50
|
+
:param code_archive_file_path: the path to the code archive file
|
51
|
+
"""
|
52
|
+
pass
|
53
|
+
|
54
|
+
@abstractmethod
|
55
|
+
def download_model_version(
|
56
|
+
self,
|
57
|
+
repository: str,
|
58
|
+
model_name: str,
|
59
|
+
version: str,
|
60
|
+
target_path: str,
|
61
|
+
namespace: Optional[str] = None,
|
62
|
+
) -> None:
|
63
|
+
"""Downloads a model from an Artifactory repository
|
64
|
+
:param repository: the repository to download the model from
|
65
|
+
:param model_name: the name of the model to download
|
66
|
+
:param version: the version of the model to download
|
67
|
+
:param target_path: the target local path to store the model in
|
68
|
+
:param namespace: the namespace of the model to download
|
69
|
+
|
70
|
+
"""
|
71
|
+
pass
|
72
|
+
|
73
|
+
def download_model_single_artifact(
|
74
|
+
self,
|
75
|
+
repository: str,
|
76
|
+
model_name: str,
|
77
|
+
version: str,
|
78
|
+
target_path: str,
|
79
|
+
source_artifact_path: str,
|
80
|
+
namespace: Optional[str] = None,
|
81
|
+
) -> None:
|
82
|
+
"""Downloads an artifact from an Artifactory repository
|
83
|
+
:param repository: the repository to download the artifact from
|
84
|
+
:param model_name: the model to download artifact from
|
85
|
+
:param version: the model's version to download artifact from
|
86
|
+
:param target_path: the target local path to store the artifact in
|
87
|
+
:param source_artifact_path: the path of the artifact under the model's version directory
|
88
|
+
:param namespace: the namespace of the artifact
|
89
|
+
"""
|
90
|
+
pass
|
91
|
+
|
92
|
+
@abstractmethod
|
93
|
+
def upload_dataset_version(
|
94
|
+
self,
|
95
|
+
repository: str,
|
96
|
+
dataset_name: str,
|
97
|
+
source_path: str,
|
98
|
+
namespace: Optional[str] = None,
|
99
|
+
version: Optional[str] = None,
|
100
|
+
tags: Optional[dict[str, str]] = None,
|
101
|
+
) -> FrogMLDatasetVersion:
|
102
|
+
"""Uploads a dataset to a repository in Artifactory. Uploaded datasets should be stored with
|
103
|
+
the following layout:
|
104
|
+
├── REPO
|
105
|
+
├── datasets
|
106
|
+
├── ${NAMESPACE}
|
107
|
+
├── ${DATASET_NAME}
|
108
|
+
├── ${DATASET_VERSION}
|
109
|
+
├── dataset-manifest.json
|
110
|
+
├── dataset
|
111
|
+
├── cli_docs_1.csv
|
112
|
+
├── cli_docs_2.csv
|
113
|
+
├── ...
|
114
|
+
:param repository: the repository to upload the dataset to
|
115
|
+
:param dataset_name: the name of the dataset
|
116
|
+
:param source_path: the source path of the dataset
|
117
|
+
:param namespace: the namespace to upload the dataset to
|
118
|
+
:param version: the version of the dataset
|
119
|
+
:param tags: tags to associate with the dataset
|
120
|
+
"""
|
121
|
+
pass
|
122
|
+
|
123
|
+
@abstractmethod
|
124
|
+
def download_dataset_version(
|
125
|
+
self,
|
126
|
+
repository: str,
|
127
|
+
dataset_name: str,
|
128
|
+
version: str,
|
129
|
+
target_path: str,
|
130
|
+
namespace: Optional[str] = None,
|
131
|
+
) -> None:
|
132
|
+
"""Downloads a dataset from an Artifactory repository
|
133
|
+
:param repository: the repository to download the dataset from
|
134
|
+
:param dataset_name: the name of the dataset to download
|
135
|
+
:param version: the version of the dataset to download
|
136
|
+
:param target_path: the target local path to store the dataset in
|
137
|
+
:param namespace: the namespace of the dataset to download
|
138
|
+
|
139
|
+
"""
|
140
|
+
pass
|
@@ -0,0 +1,56 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
home_dir = os.path.expanduser("~")
|
4
|
+
|
5
|
+
JFROG_CLI_CONFIG_FILE_PATH = os.path.join(home_dir, ".jfrog", "jfrog-cli.conf.v6")
|
6
|
+
CONFIG_FILE_PATH = os.path.join(home_dir, ".frogml", "config.json")
|
7
|
+
|
8
|
+
FROG_ML_CONFIG_USER = "user"
|
9
|
+
FROG_ML_CONFIG_ARTIFACTORY_URL = "artifactory_url"
|
10
|
+
FROG_ML_CONFIG_PASSWORD = "password" # nosec B105
|
11
|
+
FROG_ML_CONFIG_ACCESS_TOKEN = "access_token" # nosec B105
|
12
|
+
FROG_ML_DEFAULT_HTTP_THREADS_COUNT = 5
|
13
|
+
FROG_ML_MAX_CHARS_FOR_NAME = 60
|
14
|
+
|
15
|
+
SERVER_ID = "server_id"
|
16
|
+
|
17
|
+
JFROG_CLI_CONFIG_ARTIFACTORY_URL = "artifactoryUrl"
|
18
|
+
JFROG_CLI_CONFIG_URL = "url"
|
19
|
+
JFROG_CLI_CONFIG_USER = "user"
|
20
|
+
JFROG_CLI_CONFIG_PASSWORD = "password" # nosec B105
|
21
|
+
JFROG_CLI_CONFIG_ACCESS_TOKEN = "accessToken" # nosec B105
|
22
|
+
|
23
|
+
MODEL = "model"
|
24
|
+
ROOT_FROGML_MODEL_UI_DIRECTORY = "models"
|
25
|
+
MODEL_UI_DIRECTORY = "model"
|
26
|
+
MODEL_METADATA_FILE_NAME = "model-manifest.json"
|
27
|
+
BODY_PART_MODEL_MANIFEST_STREAM = "modelManifest"
|
28
|
+
|
29
|
+
DATASET = "dataset"
|
30
|
+
ROOT_FROGML_DATASET_UI_DIRECTORY = "datasets"
|
31
|
+
DATASET_UI_DIRECTORY = "dataset"
|
32
|
+
DATASET_METADATA_FILE_NAME = "dataset-manifest.json"
|
33
|
+
BODY_PART_DATASET_MANIFEST_STREAM = "datasetManifest"
|
34
|
+
|
35
|
+
CHECKSUM_SHA2_HEADER = "X-Checksum-Sha256"
|
36
|
+
|
37
|
+
JFML_THREAD_COUNT = "JFML_THREAD_COUNT"
|
38
|
+
JF_URL = "JF_URL"
|
39
|
+
JF_ACCESS_TOKEN = "JF_ACCESS_TOKEN" # nosec B105
|
40
|
+
|
41
|
+
FROG_ML_IGNORED_FILES = [
|
42
|
+
".DS_Store",
|
43
|
+
"CVS",
|
44
|
+
".cvsignore",
|
45
|
+
"SCCS",
|
46
|
+
"vssver.scc",
|
47
|
+
".svn",
|
48
|
+
".git",
|
49
|
+
".gitignore",
|
50
|
+
".gitattributes",
|
51
|
+
".gitmodules",
|
52
|
+
".gitkeep",
|
53
|
+
".gitconfig",
|
54
|
+
MODEL_METADATA_FILE_NAME,
|
55
|
+
DATASET_METADATA_FILE_NAME,
|
56
|
+
]
|