vantage6 5.0.0a34__py3-none-any.whl → 5.0.0a36__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 vantage6 might be problematic. Click here for more details.
- vantage6/cli/algorithm/generate_algorithm_json.py +9 -9
- vantage6/cli/algorithm/update.py +1 -1
- vantage6/cli/algostore/attach.py +1 -0
- vantage6/cli/algostore/files.py +3 -2
- vantage6/cli/algostore/list.py +0 -3
- vantage6/cli/algostore/new.py +83 -2
- vantage6/cli/algostore/remove.py +18 -34
- vantage6/cli/algostore/start.py +10 -7
- vantage6/cli/algostore/stop.py +12 -50
- vantage6/cli/auth/attach.py +60 -0
- vantage6/cli/auth/files.py +16 -0
- vantage6/cli/auth/list.py +13 -0
- vantage6/cli/auth/new.py +80 -0
- vantage6/cli/auth/remove.py +31 -0
- vantage6/cli/auth/start.py +80 -0
- vantage6/cli/auth/stop.py +64 -0
- vantage6/cli/cli.py +67 -37
- vantage6/cli/common/new.py +28 -3
- vantage6/cli/common/remove.py +54 -0
- vantage6/cli/common/start.py +31 -2
- vantage6/cli/common/stop.py +79 -1
- vantage6/cli/common/utils.py +47 -4
- vantage6/cli/configuration_manager.py +57 -13
- vantage6/cli/configuration_wizard.py +18 -397
- vantage6/cli/context/__init__.py +3 -0
- vantage6/cli/context/auth.py +107 -0
- vantage6/cli/context/base_server.py +0 -4
- vantage6/cli/context/node.py +10 -17
- vantage6/cli/dev/clean.py +28 -0
- vantage6/cli/dev/common.py +34 -0
- vantage6/cli/dev/rebuild.py +39 -0
- vantage6/cli/dev/start.py +36 -0
- vantage6/cli/dev/stop.py +23 -0
- vantage6/cli/globals.py +24 -1
- vantage6/cli/node/attach.py +1 -0
- vantage6/cli/node/files.py +12 -25
- vantage6/cli/node/list.py +5 -4
- vantage6/cli/node/new.py +348 -28
- vantage6/cli/node/remove.py +14 -90
- vantage6/cli/node/restart.py +30 -51
- vantage6/cli/node/start.py +81 -304
- vantage6/cli/node/stop.py +36 -96
- vantage6/cli/node/version.py +5 -4
- vantage6/cli/prometheus/monitoring_manager.py +5 -3
- vantage6/cli/rabbitmq/queue_manager.py +13 -11
- vantage6/cli/server/attach.py +1 -0
- vantage6/cli/server/common/__init__.py +1 -27
- vantage6/cli/server/import_.py +1 -1
- vantage6/cli/server/new.py +83 -2
- vantage6/cli/server/remove.py +12 -33
- vantage6/cli/server/start.py +8 -6
- vantage6/cli/server/stop.py +10 -39
- vantage6/cli/template/algo_store_config.j2 +1 -1
- vantage6/cli/template/auth_config.j2 +230 -0
- vantage6/cli/template/node_config.j2 +336 -33
- vantage6/cli/template/node_config_nonk8s.j2 +33 -0
- vantage6/cli/test/common/diagnostic_runner.py +5 -3
- vantage6/cli/use/namespace.py +2 -1
- vantage6/cli/utils.py +0 -2
- {vantage6-5.0.0a34.dist-info → vantage6-5.0.0a36.dist-info}/METADATA +3 -3
- vantage6-5.0.0a36.dist-info/RECORD +86 -0
- vantage6/cli/dev/create.py +0 -693
- vantage6/cli/dev/data/km_dataset.csv +0 -2401
- vantage6/cli/dev/remove.py +0 -112
- vantage6/cli/node/clean.py +0 -46
- vantage6/cli/server/shell.py +0 -54
- vantage6-5.0.0a34.dist-info/RECORD +0 -75
- {vantage6-5.0.0a34.dist-info → vantage6-5.0.0a36.dist-info}/WHEEL +0 -0
- {vantage6-5.0.0a34.dist-info → vantage6-5.0.0a36.dist-info}/entry_points.txt +0 -0
|
@@ -1,290 +1,25 @@
|
|
|
1
|
-
import os
|
|
2
1
|
from pathlib import Path
|
|
3
|
-
from typing import Any
|
|
4
2
|
|
|
5
3
|
import questionary as q
|
|
6
4
|
|
|
7
|
-
from vantage6.common import error, info
|
|
8
|
-
from vantage6.common.client.node_client import NodeClient
|
|
5
|
+
from vantage6.common import error, info
|
|
9
6
|
from vantage6.common.context import AppContext
|
|
10
7
|
from vantage6.common.globals import (
|
|
11
|
-
DATABASE_TYPES,
|
|
12
8
|
DEFAULT_API_PATH,
|
|
13
9
|
InstanceType,
|
|
14
|
-
NodePolicy,
|
|
15
10
|
Ports,
|
|
16
|
-
RequiredNodeEnvVars,
|
|
17
11
|
)
|
|
18
12
|
|
|
19
|
-
from vantage6.cli.config import CliConfig
|
|
20
13
|
from vantage6.cli.configuration_manager import (
|
|
21
14
|
AlgorithmStoreConfigurationManager,
|
|
15
|
+
AuthConfigurationManager,
|
|
22
16
|
NodeConfigurationManager,
|
|
23
17
|
ServerConfigurationManager,
|
|
24
18
|
)
|
|
25
19
|
from vantage6.cli.context import select_context_class
|
|
26
20
|
|
|
27
21
|
|
|
28
|
-
def
|
|
29
|
-
"""
|
|
30
|
-
Questionary to generate a config file for the node instance.
|
|
31
|
-
|
|
32
|
-
Parameters
|
|
33
|
-
----------
|
|
34
|
-
dirs : dict
|
|
35
|
-
Dictionary with the directories of the node instance.
|
|
36
|
-
instance_name : str
|
|
37
|
-
Name of the node instance.
|
|
38
|
-
|
|
39
|
-
Returns
|
|
40
|
-
-------
|
|
41
|
-
dict
|
|
42
|
-
Dictionary with the new node configuration
|
|
43
|
-
"""
|
|
44
|
-
config = q.unsafe_prompt(
|
|
45
|
-
[
|
|
46
|
-
{"type": "text", "name": "api_key", "message": "Enter given api-key:"},
|
|
47
|
-
{
|
|
48
|
-
"type": "text",
|
|
49
|
-
"name": "server_url",
|
|
50
|
-
"message": "The base-URL of the server:",
|
|
51
|
-
"default": "http://localhost",
|
|
52
|
-
},
|
|
53
|
-
]
|
|
54
|
-
)
|
|
55
|
-
# remove trailing slash from server_url if entered by user
|
|
56
|
-
config["server_url"] = config["server_url"].rstrip("/")
|
|
57
|
-
|
|
58
|
-
# set default port to the https port if server_url is https
|
|
59
|
-
default_port = (
|
|
60
|
-
str(Ports.HTTPS)
|
|
61
|
-
if config["server_url"].startswith("https")
|
|
62
|
-
else str(Ports.DEV_SERVER)
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
config = config | q.unsafe_prompt(
|
|
66
|
-
[
|
|
67
|
-
{
|
|
68
|
-
"type": "text",
|
|
69
|
-
"name": "port",
|
|
70
|
-
"message": "Enter port to which the server listens:",
|
|
71
|
-
"default": default_port,
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
"type": "text",
|
|
75
|
-
"name": "api_path",
|
|
76
|
-
"message": "Path of the api:",
|
|
77
|
-
"default": "/api",
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"type": "text",
|
|
81
|
-
"name": "task_dir",
|
|
82
|
-
"message": "Task directory path:",
|
|
83
|
-
"default": str(dirs["data"]),
|
|
84
|
-
},
|
|
85
|
-
]
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
config["databases"] = list()
|
|
89
|
-
while q.confirm("Do you want to add a database?").unsafe_ask():
|
|
90
|
-
db_label = q.unsafe_prompt(
|
|
91
|
-
[
|
|
92
|
-
{
|
|
93
|
-
"type": "text",
|
|
94
|
-
"name": "label",
|
|
95
|
-
"message": "Enter unique label for the database:",
|
|
96
|
-
"default": "default",
|
|
97
|
-
}
|
|
98
|
-
]
|
|
99
|
-
)
|
|
100
|
-
db_path = q.unsafe_prompt(
|
|
101
|
-
[{"type": "text", "name": "uri", "message": "Database URI:"}]
|
|
102
|
-
)
|
|
103
|
-
db_type = q.select("Database type:", choices=DATABASE_TYPES).unsafe_ask()
|
|
104
|
-
|
|
105
|
-
config["databases"].append(
|
|
106
|
-
{"label": db_label.get("label"), "uri": db_path.get("uri"), "type": db_type}
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
is_add_vpn = q.confirm(
|
|
110
|
-
"Do you want to connect to a VPN server?", default=False
|
|
111
|
-
).unsafe_ask()
|
|
112
|
-
if is_add_vpn:
|
|
113
|
-
config["vpn_subnet"] = q.text(
|
|
114
|
-
message="Subnet of the VPN server you want to connect to:",
|
|
115
|
-
default="10.76.0.0/16",
|
|
116
|
-
).unsafe_ask()
|
|
117
|
-
|
|
118
|
-
is_policies = q.confirm(
|
|
119
|
-
"Do you want to limit the algorithms allowed to run on your node? This "
|
|
120
|
-
"should always be done for production scenarios.",
|
|
121
|
-
default=True,
|
|
122
|
-
).unsafe_ask()
|
|
123
|
-
policies = {}
|
|
124
|
-
if is_policies:
|
|
125
|
-
info(
|
|
126
|
-
"You can limit the algorithms that can run on your node in two ways: by "
|
|
127
|
-
"allowing specific algorithms or by allowing all algorithms in a given "
|
|
128
|
-
"algorithm store."
|
|
129
|
-
)
|
|
130
|
-
ask_single_algorithms = q.confirm(
|
|
131
|
-
"Do you want to enter a list of allowed algorithms?"
|
|
132
|
-
).unsafe_ask()
|
|
133
|
-
if ask_single_algorithms:
|
|
134
|
-
policies[NodePolicy.ALLOWED_ALGORITHMS.value] = _get_allowed_algorithms()
|
|
135
|
-
ask_algorithm_stores = q.confirm(
|
|
136
|
-
"Do you want to allow algorithms from specific algorithm stores?"
|
|
137
|
-
).unsafe_ask()
|
|
138
|
-
if ask_algorithm_stores:
|
|
139
|
-
policies[NodePolicy.ALLOWED_ALGORITHM_STORES.value] = (
|
|
140
|
-
_get_allowed_algorithm_stores()
|
|
141
|
-
)
|
|
142
|
-
if ask_single_algorithms and ask_algorithm_stores:
|
|
143
|
-
require_both_whitelists = q.confirm(
|
|
144
|
-
"Do you want to allow only algorithms that are both in the list of "
|
|
145
|
-
"allowed algorithms *AND* are part of one of the allowed algorithm "
|
|
146
|
-
"stores? If not, algorithms will be allowed if they are in either the "
|
|
147
|
-
"list of allowed algorithms or one of the allowed algorithm stores.",
|
|
148
|
-
default=True,
|
|
149
|
-
).unsafe_ask()
|
|
150
|
-
policies["allow_either_whitelist_or_store"] = not require_both_whitelists
|
|
151
|
-
if policies:
|
|
152
|
-
config["policies"] = policies
|
|
153
|
-
|
|
154
|
-
res = q.select(
|
|
155
|
-
"Which level of logging would you like?",
|
|
156
|
-
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "NOTSET"],
|
|
157
|
-
).unsafe_ask()
|
|
158
|
-
|
|
159
|
-
config["logging"] = {
|
|
160
|
-
"level": res,
|
|
161
|
-
"use_console": True,
|
|
162
|
-
"backup_count": 5,
|
|
163
|
-
"max_size": 1024,
|
|
164
|
-
"format": "%(asctime)s - %(name)-14s - %(levelname)-8s - %(message)s",
|
|
165
|
-
"datefmt": "%Y-%m-%d %H:%M:%S",
|
|
166
|
-
"loggers": [
|
|
167
|
-
{"name": "urllib3", "level": "warning"},
|
|
168
|
-
{"name": "requests", "level": "warning"},
|
|
169
|
-
{"name": "engineio.client", "level": "warning"},
|
|
170
|
-
{"name": "docker.utils.config", "level": "warning"},
|
|
171
|
-
{"name": "docker.auth", "level": "warning"},
|
|
172
|
-
],
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
# Check if we can login to the server to retrieve collaboration settings
|
|
176
|
-
client = NodeClient(
|
|
177
|
-
instance_name,
|
|
178
|
-
config["api_key"],
|
|
179
|
-
server_url=f"{config['server_url']}:{config['port']}{config['api_path']}",
|
|
180
|
-
auth_url=os.environ.get(RequiredNodeEnvVars.KEYCLOAK_URL.value),
|
|
181
|
-
)
|
|
182
|
-
try:
|
|
183
|
-
client.authenticate()
|
|
184
|
-
except Exception as e:
|
|
185
|
-
error(f"Could not authenticate with server: {e}")
|
|
186
|
-
error("Please check (1) your API key and (2) if your server is online")
|
|
187
|
-
warning(
|
|
188
|
-
"If you continue, you should provide your collaboration settings manually."
|
|
189
|
-
)
|
|
190
|
-
if q.confirm("Do you want to abort?", default=True).unsafe_ask():
|
|
191
|
-
exit(0)
|
|
192
|
-
|
|
193
|
-
if client.whoami is not None:
|
|
194
|
-
encryption = client.is_encrypted_collaboration()
|
|
195
|
-
# TODO when we build collaboration policies, update this to provide
|
|
196
|
-
# the node admin with a list of all policies, and whether or not
|
|
197
|
-
# to accept them
|
|
198
|
-
q.confirm(
|
|
199
|
-
f"Encryption is {'enabled' if encryption else 'disabled'}"
|
|
200
|
-
f" for this collaboration. Accept?",
|
|
201
|
-
default=True,
|
|
202
|
-
).unsafe_ask()
|
|
203
|
-
else:
|
|
204
|
-
encryption = q.confirm("Enable encryption?", default=True).unsafe_ask()
|
|
205
|
-
|
|
206
|
-
private_key = (
|
|
207
|
-
"" if not encryption else q.text("Path to private key file:").unsafe_ask()
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
config["encryption"] = {
|
|
211
|
-
"enabled": encryption is True or encryption == "true",
|
|
212
|
-
"private_key": private_key,
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return config
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
def _get_allowed_algorithms() -> list[str]:
|
|
219
|
-
"""
|
|
220
|
-
Prompt the user for the allowed algorithms on their node
|
|
221
|
-
|
|
222
|
-
Returns
|
|
223
|
-
-------
|
|
224
|
-
list[str]
|
|
225
|
-
List of allowed algorithms or regular expressions to match them
|
|
226
|
-
"""
|
|
227
|
-
info("Below you can add algorithms that are allowed to run on your node.")
|
|
228
|
-
info(
|
|
229
|
-
"You can use regular expressions to match multiple algorithms, or you can "
|
|
230
|
-
"use strings to provide one algorithm at a time."
|
|
231
|
-
)
|
|
232
|
-
info("Examples:")
|
|
233
|
-
info(r"^harbor2\.vantage6\.ai/demo/average$ Allow the demo average algorithm")
|
|
234
|
-
info(
|
|
235
|
-
r"^harbor2\.vantage6\.ai/algorithms/.* Allow all algorithms from "
|
|
236
|
-
"harbor2.vantage6.ai/algorithms"
|
|
237
|
-
)
|
|
238
|
-
info(
|
|
239
|
-
r"^harbor2\.vantage6\.ai/demo/average@sha256:82becede...$ Allow a "
|
|
240
|
-
"specific hash of average algorithm"
|
|
241
|
-
)
|
|
242
|
-
allowed_algorithms = []
|
|
243
|
-
while True:
|
|
244
|
-
algo = q.text(message="Enter your algorithm expression:").unsafe_ask()
|
|
245
|
-
allowed_algorithms.append(algo)
|
|
246
|
-
if not q.confirm(
|
|
247
|
-
"Do you want to add another algorithm expression?", default=True
|
|
248
|
-
).unsafe_ask():
|
|
249
|
-
break
|
|
250
|
-
return allowed_algorithms
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
def _get_allowed_algorithm_stores() -> list[str]:
|
|
254
|
-
"""
|
|
255
|
-
Prompt the user for the allowed algorithm stores on their node
|
|
256
|
-
|
|
257
|
-
Returns
|
|
258
|
-
-------
|
|
259
|
-
list[str]
|
|
260
|
-
List of allowed algorithm stores
|
|
261
|
-
"""
|
|
262
|
-
info("Below you can add algorithm stores that are allowed to run on your node.")
|
|
263
|
-
info(
|
|
264
|
-
"You can use regular expressions to match multiple algorithm stores, or you can"
|
|
265
|
-
" use strings to provide one algorithm store at a time."
|
|
266
|
-
)
|
|
267
|
-
info("Examples:")
|
|
268
|
-
info(
|
|
269
|
-
"https://store.cotopaxi.vantage6.ai Allow all algorithms from the "
|
|
270
|
-
"community store"
|
|
271
|
-
)
|
|
272
|
-
info(
|
|
273
|
-
r"^https://*\.vantage6\.ai$ Allow all algorithms from any "
|
|
274
|
-
"store hosted on vantage6.ai"
|
|
275
|
-
)
|
|
276
|
-
allowed_algorithm_stores = []
|
|
277
|
-
while True:
|
|
278
|
-
store = q.text(message="Enter the URL of the algorithm store:").unsafe_ask()
|
|
279
|
-
allowed_algorithm_stores.append(store)
|
|
280
|
-
if not q.confirm(
|
|
281
|
-
"Do you want to add another algorithm store?", default=True
|
|
282
|
-
).unsafe_ask():
|
|
283
|
-
break
|
|
284
|
-
return allowed_algorithm_stores
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
def _add_common_server_config(
|
|
22
|
+
def add_common_server_config(
|
|
288
23
|
config: dict, instance_type: InstanceType, instance_name: str
|
|
289
24
|
) -> dict:
|
|
290
25
|
"""
|
|
@@ -364,60 +99,6 @@ def _add_common_server_config(
|
|
|
364
99
|
return config, is_production
|
|
365
100
|
|
|
366
101
|
|
|
367
|
-
def server_configuration_questionaire(instance_name: str) -> dict[str, Any]:
|
|
368
|
-
"""
|
|
369
|
-
Kubernetes-specific questionnaire to generate Helm values for server.
|
|
370
|
-
|
|
371
|
-
Parameters
|
|
372
|
-
----------
|
|
373
|
-
instance_name : str
|
|
374
|
-
Name of the server instance.
|
|
375
|
-
|
|
376
|
-
Returns
|
|
377
|
-
-------
|
|
378
|
-
dict[str, Any]
|
|
379
|
-
dictionary with Helm values for the server configuration
|
|
380
|
-
"""
|
|
381
|
-
# Get active kube namespace
|
|
382
|
-
cli_config = CliConfig()
|
|
383
|
-
kube_namespace = cli_config.get_last_namespace()
|
|
384
|
-
|
|
385
|
-
# Initialize config with basic structure
|
|
386
|
-
config = {"server": {}, "database": {}, "ui": {}, "rabbitmq": {}}
|
|
387
|
-
|
|
388
|
-
config, is_production = _add_common_server_config(
|
|
389
|
-
config, InstanceType.SERVER, instance_name
|
|
390
|
-
)
|
|
391
|
-
if not is_production:
|
|
392
|
-
config["server"]["jwt"] = {
|
|
393
|
-
"secret": "constant_development_secret`",
|
|
394
|
-
}
|
|
395
|
-
config["server"]["dev"] = {
|
|
396
|
-
"host_uri": "host.docker.internal",
|
|
397
|
-
"store_in_local_cluster": True,
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
# TODO v5+ these should be removed, latest should usually be used so question is
|
|
401
|
-
# not needed. However, for now we want to specify alpha/beta images.
|
|
402
|
-
# === Server settings ===
|
|
403
|
-
config["server"]["image"] = q.text(
|
|
404
|
-
"Server Docker image:",
|
|
405
|
-
default="harbor2.vantage6.ai/infrastructure/server:latest",
|
|
406
|
-
).unsafe_ask()
|
|
407
|
-
|
|
408
|
-
# === UI settings ===
|
|
409
|
-
config["ui"]["image"] = q.text(
|
|
410
|
-
"UI Docker image:",
|
|
411
|
-
default="harbor2.vantage6.ai/infrastructure/ui:latest",
|
|
412
|
-
).unsafe_ask()
|
|
413
|
-
|
|
414
|
-
# === Keycloak settings ===
|
|
415
|
-
keycloak_url = f"http://vantage6-auth-keycloak.{kube_namespace}.svc.cluster.local"
|
|
416
|
-
config["server"]["keycloakUrl"] = keycloak_url
|
|
417
|
-
|
|
418
|
-
return config
|
|
419
|
-
|
|
420
|
-
|
|
421
102
|
def _add_production_server_config(config: dict) -> dict:
|
|
422
103
|
"""
|
|
423
104
|
Add the production server configuration to the config
|
|
@@ -445,75 +126,8 @@ def _add_production_server_config(config: dict) -> dict:
|
|
|
445
126
|
return config
|
|
446
127
|
|
|
447
128
|
|
|
448
|
-
def algo_store_configuration_questionaire(instance_name: str) -> dict:
|
|
449
|
-
"""
|
|
450
|
-
Questionary to generate a config file for the algorithm store server
|
|
451
|
-
instance.
|
|
452
|
-
|
|
453
|
-
Parameters
|
|
454
|
-
----------
|
|
455
|
-
instance_name : str
|
|
456
|
-
Name of the server instance.
|
|
457
|
-
|
|
458
|
-
Returns
|
|
459
|
-
-------
|
|
460
|
-
dict
|
|
461
|
-
Dictionary with the new server configuration
|
|
462
|
-
"""
|
|
463
|
-
config = {"store": {}, "database": {}}
|
|
464
|
-
|
|
465
|
-
config, is_production = _add_common_server_config(
|
|
466
|
-
config, InstanceType.ALGORITHM_STORE, instance_name
|
|
467
|
-
)
|
|
468
|
-
if not is_production:
|
|
469
|
-
config["store"]["dev"] = {
|
|
470
|
-
"host_uri": "host.docker.internal",
|
|
471
|
-
"disable_review": True,
|
|
472
|
-
"review_own_algorithm": True,
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
default_v6_server_uri = f"http://localhost:{Ports.DEV_SERVER}{DEFAULT_API_PATH}"
|
|
476
|
-
default_root_username = "admin"
|
|
477
|
-
|
|
478
|
-
v6_server_uri = q.text(
|
|
479
|
-
"What is the Vantage6 server linked to the algorithm store? "
|
|
480
|
-
"Provide the link to the server endpoint.",
|
|
481
|
-
default=default_v6_server_uri,
|
|
482
|
-
).unsafe_ask()
|
|
483
|
-
|
|
484
|
-
root_username = q.text(
|
|
485
|
-
"What is the username of the root user?",
|
|
486
|
-
default=default_root_username,
|
|
487
|
-
).unsafe_ask()
|
|
488
|
-
|
|
489
|
-
config["root_user"] = {
|
|
490
|
-
"v6_server_uri": v6_server_uri,
|
|
491
|
-
"username": root_username,
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
# ask about openness of the algorithm store
|
|
495
|
-
config["policies"] = {}
|
|
496
|
-
is_open = q.confirm(
|
|
497
|
-
"Do you want to open the algorithm store to the public? This will allow anyone "
|
|
498
|
-
"to view the algorithms, but they cannot modify them.",
|
|
499
|
-
default=False,
|
|
500
|
-
).unsafe_ask()
|
|
501
|
-
if is_open:
|
|
502
|
-
open_algos_policy = "public"
|
|
503
|
-
else:
|
|
504
|
-
is_open_to_whitelist = q.confirm(
|
|
505
|
-
"Do you want to allow all authenticated users to access "
|
|
506
|
-
"the algorithms in the store? If not allowing this, you will have to assign"
|
|
507
|
-
" the appropriate permissions to each user individually.",
|
|
508
|
-
default=True,
|
|
509
|
-
).unsafe_ask()
|
|
510
|
-
open_algos_policy = "authenticated" if is_open_to_whitelist else "private"
|
|
511
|
-
config["policies"]["algorithm_view"] = open_algos_policy
|
|
512
|
-
|
|
513
|
-
return config
|
|
514
|
-
|
|
515
|
-
|
|
516
129
|
def configuration_wizard(
|
|
130
|
+
questionnaire_function: callable,
|
|
517
131
|
type_: InstanceType,
|
|
518
132
|
instance_name: str,
|
|
519
133
|
system_folders: bool,
|
|
@@ -523,6 +137,8 @@ def configuration_wizard(
|
|
|
523
137
|
|
|
524
138
|
Parameters
|
|
525
139
|
----------
|
|
140
|
+
questionnaire_function : callable
|
|
141
|
+
Function to generate the configuration
|
|
526
142
|
type_ : InstanceType
|
|
527
143
|
Type of the instance to create a configuration for
|
|
528
144
|
instance_name : str
|
|
@@ -540,19 +156,24 @@ def configuration_wizard(
|
|
|
540
156
|
|
|
541
157
|
# invoke questionaire to create configuration file
|
|
542
158
|
if type_ == InstanceType.NODE:
|
|
543
|
-
|
|
544
|
-
config = node_configuration_questionaire(dirs, instance_name)
|
|
545
|
-
elif type_ == InstanceType.SERVER:
|
|
546
|
-
conf_manager = ServerConfigurationManager
|
|
547
|
-
config = server_configuration_questionaire(instance_name)
|
|
159
|
+
config = questionnaire_function(dirs, instance_name)
|
|
548
160
|
else:
|
|
549
|
-
|
|
550
|
-
config = algo_store_configuration_questionaire(instance_name)
|
|
161
|
+
config = questionnaire_function(instance_name)
|
|
551
162
|
|
|
552
163
|
# in the case of an environment we need to add it to the current
|
|
553
164
|
# configuration. In the case of application we can simply overwrite this
|
|
554
165
|
# key (although there might be environments present)
|
|
555
166
|
config_file = Path(dirs.get("config")) / (instance_name + ".yaml")
|
|
167
|
+
if type_ == InstanceType.NODE:
|
|
168
|
+
conf_manager = NodeConfigurationManager
|
|
169
|
+
elif type_ == InstanceType.SERVER:
|
|
170
|
+
conf_manager = ServerConfigurationManager
|
|
171
|
+
elif type_ == InstanceType.ALGORITHM_STORE:
|
|
172
|
+
conf_manager = AlgorithmStoreConfigurationManager
|
|
173
|
+
elif type_ == InstanceType.AUTH:
|
|
174
|
+
conf_manager = AuthConfigurationManager
|
|
175
|
+
else:
|
|
176
|
+
raise ValueError(f"Invalid instance type: {type_}")
|
|
556
177
|
|
|
557
178
|
if Path(config_file).exists():
|
|
558
179
|
config_manager = conf_manager.from_file(config_file)
|
vantage6/cli/context/__init__.py
CHANGED
|
@@ -14,6 +14,7 @@ from vantage6.common import error
|
|
|
14
14
|
from vantage6.common.globals import InstanceType
|
|
15
15
|
|
|
16
16
|
from vantage6.cli.context.algorithm_store import AlgorithmStoreContext
|
|
17
|
+
from vantage6.cli.context.auth import AuthContext
|
|
17
18
|
from vantage6.cli.context.node import NodeContext
|
|
18
19
|
from vantage6.cli.context.server import ServerContext
|
|
19
20
|
|
|
@@ -45,6 +46,8 @@ def select_context_class(
|
|
|
45
46
|
return AlgorithmStoreContext
|
|
46
47
|
elif type_ == InstanceType.NODE:
|
|
47
48
|
return NodeContext
|
|
49
|
+
elif type_ == InstanceType.AUTH:
|
|
50
|
+
return AuthContext
|
|
48
51
|
else:
|
|
49
52
|
raise NotImplementedError
|
|
50
53
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from vantage6.common.context import AppContext
|
|
4
|
+
from vantage6.common.globals import InstanceType
|
|
5
|
+
|
|
6
|
+
from vantage6.cli import __version__
|
|
7
|
+
from vantage6.cli.configuration_manager import AuthConfigurationManager
|
|
8
|
+
from vantage6.cli.globals import DEFAULT_SERVER_SYSTEM_FOLDERS as S_FOL
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AuthContext(AppContext):
|
|
12
|
+
"""
|
|
13
|
+
Context class for the keycloak authentication server.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
instance_name : str
|
|
18
|
+
Name of the configuration instance, corresponds to the filename
|
|
19
|
+
of the configuration file.
|
|
20
|
+
system_folders : bool, optional
|
|
21
|
+
System wide or user configuration, by default S_FOL
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
# The auth configuration manager is aware of the structure of the auth
|
|
25
|
+
# configuration file and makes sure only valid configuration can be loaded.
|
|
26
|
+
INST_CONFIG_MANAGER = AuthConfigurationManager
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
instance_name: str,
|
|
31
|
+
system_folders: bool = S_FOL,
|
|
32
|
+
):
|
|
33
|
+
super().__init__(
|
|
34
|
+
InstanceType.AUTH,
|
|
35
|
+
instance_name,
|
|
36
|
+
system_folders=system_folders,
|
|
37
|
+
)
|
|
38
|
+
self.log.info("vantage6 version '%s'", __version__)
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def from_external_config_file(
|
|
42
|
+
cls, path: str, system_folders: bool = S_FOL
|
|
43
|
+
) -> AuthContext:
|
|
44
|
+
"""
|
|
45
|
+
Create a server context from an external configuration file. External
|
|
46
|
+
means that the configuration file is not located in the default folders
|
|
47
|
+
but its location is specified by the user.
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
path : str
|
|
52
|
+
Path of the configuration file
|
|
53
|
+
system_folders : bool, optional
|
|
54
|
+
System wide or user configuration, by default S_FOL
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
ServerContext
|
|
59
|
+
Server context object
|
|
60
|
+
"""
|
|
61
|
+
return super().from_external_config_file(
|
|
62
|
+
path,
|
|
63
|
+
InstanceType.AUTH,
|
|
64
|
+
system_folders,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def config_exists(cls, instance_name: str, system_folders: bool = S_FOL) -> bool:
|
|
69
|
+
"""
|
|
70
|
+
Check if a configuration file exists.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
instance_name : str
|
|
75
|
+
Name of the configuration instance, corresponds to the filename
|
|
76
|
+
of the configuration file.
|
|
77
|
+
system_folders : bool, optional
|
|
78
|
+
System wide or user configuration, by default S_FOL
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
bool
|
|
83
|
+
Whether the configuration file exists or not
|
|
84
|
+
"""
|
|
85
|
+
return super().config_exists(
|
|
86
|
+
InstanceType.AUTH, instance_name, system_folders=system_folders
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
def available_configurations(
|
|
91
|
+
cls, system_folders: bool = S_FOL
|
|
92
|
+
) -> tuple[list, list]:
|
|
93
|
+
"""
|
|
94
|
+
Find all available auth configurations in the default folders.
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
system_folders : bool, optional
|
|
99
|
+
System wide or user configuration, by default S_FOL
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
tuple[list, list]
|
|
104
|
+
The first list contains validated configuration files, the second
|
|
105
|
+
list contains invalid configuration files.
|
|
106
|
+
"""
|
|
107
|
+
return super().available_configurations(InstanceType.AUTH, system_folders)
|
vantage6/cli/context/node.py
CHANGED
|
@@ -150,7 +150,10 @@ class NodeContext(AppContext):
|
|
|
150
150
|
dictionary with database names as keys and their corresponding
|
|
151
151
|
paths as values.
|
|
152
152
|
"""
|
|
153
|
-
|
|
153
|
+
if self.in_container:
|
|
154
|
+
return self.config["databases"]
|
|
155
|
+
else:
|
|
156
|
+
return self.config["node"]["databases"]
|
|
154
157
|
|
|
155
158
|
@property
|
|
156
159
|
def docker_container_name(self) -> str:
|
|
@@ -230,19 +233,6 @@ class NodeContext(AppContext):
|
|
|
230
233
|
self.data_dir = dirs.get("data")
|
|
231
234
|
self.config_dir = dirs.get("config")
|
|
232
235
|
|
|
233
|
-
@staticmethod
|
|
234
|
-
def instance_folders(*_args, **_kwargs) -> dict:
|
|
235
|
-
"""Log, data and config folders are always mounted. The node manager
|
|
236
|
-
should take care of this.
|
|
237
|
-
"""
|
|
238
|
-
mnt = Path("/mnt")
|
|
239
|
-
|
|
240
|
-
return {
|
|
241
|
-
"log": mnt / "log",
|
|
242
|
-
"data": mnt / "data",
|
|
243
|
-
"config": mnt / "config",
|
|
244
|
-
}
|
|
245
|
-
|
|
246
236
|
def __create_node_identifier(self) -> str:
|
|
247
237
|
"""
|
|
248
238
|
Create a unique identifier for the node.
|
|
@@ -252,6 +242,9 @@ class NodeContext(AppContext):
|
|
|
252
242
|
str
|
|
253
243
|
Unique identifier for the node
|
|
254
244
|
"""
|
|
255
|
-
|
|
256
|
-
os.environ.get("V6_API_KEY")
|
|
257
|
-
|
|
245
|
+
if self.in_container:
|
|
246
|
+
api_key = os.environ.get("V6_API_KEY")
|
|
247
|
+
else:
|
|
248
|
+
api_key = self.config["node"]["apiKey"]
|
|
249
|
+
|
|
250
|
+
return hashlib.sha256(api_key.encode(STRING_ENCODING)).hexdigest()[:16]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
|
|
6
|
+
from vantage6.common import error, info
|
|
7
|
+
|
|
8
|
+
from vantage6.cli.dev.common import check_devspace_installed
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.command()
|
|
12
|
+
def cli_clean_dev_env():
|
|
13
|
+
"""
|
|
14
|
+
Stops and cleans up the development environment.
|
|
15
|
+
|
|
16
|
+
Removes the kubernetes resources and local data (e.g. tasks data, database data).
|
|
17
|
+
This is useful when you want to start from scratch.
|
|
18
|
+
"""
|
|
19
|
+
check_devspace_installed()
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
info("🧹 Cleaning development environment with devspace...")
|
|
23
|
+
cmd = ["devspace", "run", "purge"]
|
|
24
|
+
subprocess.run(cmd, check=True, capture_output=False)
|
|
25
|
+
info("✅ Development environment cleaned successfully!")
|
|
26
|
+
except subprocess.CalledProcessError as e:
|
|
27
|
+
error(f"❌ Error cleaning development environment: {e}")
|
|
28
|
+
sys.exit(e.returncode)
|