vantage6 5.0.0a34__py3-none-any.whl → 5.0.0a35__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/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/cli.py +31 -33
- vantage6/cli/common/new.py +28 -3
- vantage6/cli/common/remove.py +54 -0
- vantage6/cli/common/start.py +30 -1
- vantage6/cli/common/stop.py +79 -1
- vantage6/cli/common/utils.py +45 -4
- vantage6/cli/configuration_manager.py +26 -13
- vantage6/cli/configuration_wizard.py +13 -397
- vantage6/cli/context/node.py +10 -17
- vantage6/cli/globals.py +20 -0
- vantage6/cli/node/attach.py +1 -0
- vantage6/cli/node/files.py +12 -25
- 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/server/new.py +70 -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/node_config.j2 +336 -33
- vantage6/cli/utils.py +0 -2
- {vantage6-5.0.0a34.dist-info → vantage6-5.0.0a35.dist-info}/METADATA +3 -3
- {vantage6-5.0.0a34.dist-info → vantage6-5.0.0a35.dist-info}/RECORD +32 -32
- vantage6/cli/node/clean.py +0 -46
- {vantage6-5.0.0a34.dist-info → vantage6-5.0.0a35.dist-info}/WHEEL +0 -0
- {vantage6-5.0.0a34.dist-info → vantage6-5.0.0a35.dist-info}/entry_points.txt +0 -0
vantage6/cli/common/utils.py
CHANGED
|
@@ -14,6 +14,7 @@ from vantage6.common.globals import APPNAME, STRING_ENCODING, InstanceType
|
|
|
14
14
|
|
|
15
15
|
from vantage6.cli.config import CliConfig
|
|
16
16
|
from vantage6.cli.context import select_context_class
|
|
17
|
+
from vantage6.cli.globals import CLICommandName
|
|
17
18
|
from vantage6.cli.utils import validate_input_cmd_args
|
|
18
19
|
|
|
19
20
|
|
|
@@ -173,7 +174,7 @@ def select_running_service(
|
|
|
173
174
|
"""
|
|
174
175
|
try:
|
|
175
176
|
name = q.select(
|
|
176
|
-
f"Select
|
|
177
|
+
f"Select a {instance_type.value}:",
|
|
177
178
|
choices=running_services,
|
|
178
179
|
).unsafe_ask()
|
|
179
180
|
except KeyboardInterrupt:
|
|
@@ -340,6 +341,16 @@ def get_name_from_container_name(container_name: str) -> str:
|
|
|
340
341
|
return "-".join(container_name.split("-")[1:-1])
|
|
341
342
|
|
|
342
343
|
|
|
344
|
+
def get_config_name_from_service_name(service_name: str) -> str:
|
|
345
|
+
"""
|
|
346
|
+
Get the config name from a service name.
|
|
347
|
+
"""
|
|
348
|
+
# helm release name is structured as:
|
|
349
|
+
# f"{APPNAME}-{name}-{scope}-{instance_type}"
|
|
350
|
+
# we want to get the name from the service name
|
|
351
|
+
return "-".join(service_name.split("-")[1:-2])
|
|
352
|
+
|
|
353
|
+
|
|
343
354
|
def attach_logs(*labels: list[str]) -> None:
|
|
344
355
|
"""
|
|
345
356
|
Attach to the logs of the given labels.
|
|
@@ -364,10 +375,40 @@ def get_main_cli_command_name(instance_type: InstanceType) -> str:
|
|
|
364
375
|
The type of instance to get the main CLI command name for
|
|
365
376
|
"""
|
|
366
377
|
if instance_type == InstanceType.SERVER:
|
|
367
|
-
return
|
|
378
|
+
return CLICommandName.SERVER.value
|
|
368
379
|
elif instance_type == InstanceType.ALGORITHM_STORE:
|
|
369
|
-
return
|
|
380
|
+
return CLICommandName.ALGORITHM_STORE.value
|
|
370
381
|
elif instance_type == InstanceType.NODE:
|
|
371
|
-
return
|
|
382
|
+
return CLICommandName.NODE.value
|
|
372
383
|
else:
|
|
373
384
|
raise ValueError(f"Invalid instance type: {instance_type}")
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def check_running(
|
|
388
|
+
helm_release_name: str, instance_type: InstanceType, name: str, system_folders: bool
|
|
389
|
+
) -> bool:
|
|
390
|
+
"""
|
|
391
|
+
Check if the instance is already running.
|
|
392
|
+
|
|
393
|
+
Parameters
|
|
394
|
+
----------
|
|
395
|
+
helm_release_name : str
|
|
396
|
+
The name of the Helm release.
|
|
397
|
+
instance_type : InstanceType
|
|
398
|
+
The type of instance to check
|
|
399
|
+
name : str
|
|
400
|
+
The name of the instance to check
|
|
401
|
+
system_folders : bool
|
|
402
|
+
Whether to use system folders or not
|
|
403
|
+
|
|
404
|
+
Returns
|
|
405
|
+
-------
|
|
406
|
+
bool
|
|
407
|
+
True if the instance is already running, False otherwise
|
|
408
|
+
"""
|
|
409
|
+
running_services = find_running_service_names(
|
|
410
|
+
instance_type=instance_type,
|
|
411
|
+
only_system_folders=system_folders,
|
|
412
|
+
only_user_folders=not system_folders,
|
|
413
|
+
)
|
|
414
|
+
return helm_release_name in running_services
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
from typing import Self
|
|
2
2
|
|
|
3
|
-
from schema import And,
|
|
3
|
+
from schema import And, Use
|
|
4
4
|
|
|
5
5
|
from vantage6.common.configuration_manager import Configuration, ConfigurationManager
|
|
6
6
|
|
|
7
|
-
from vantage6.cli.globals import
|
|
7
|
+
from vantage6.cli.globals import (
|
|
8
|
+
ALGO_STORE_TEMPLATE_FILE,
|
|
9
|
+
NODE_TEMPLATE_FILE,
|
|
10
|
+
SERVER_TEMPLATE_FILE,
|
|
11
|
+
)
|
|
8
12
|
|
|
9
13
|
LOGGING_VALIDATORS = {
|
|
10
14
|
"level": And(
|
|
@@ -44,17 +48,20 @@ class NodeConfiguration(Configuration):
|
|
|
44
48
|
"""
|
|
45
49
|
|
|
46
50
|
VALIDATORS = {
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
#
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
Optional("
|
|
56
|
-
Optional("
|
|
57
|
-
Optional("
|
|
51
|
+
# # TODO enable validators for node. To see if it works, use v6 node list
|
|
52
|
+
# "node": {
|
|
53
|
+
# "server_url": Use(str),
|
|
54
|
+
# "port": Or(Use(int), None),
|
|
55
|
+
# "task_dir": Use(str),
|
|
56
|
+
# # TODO: remove `dict` validation from databases
|
|
57
|
+
# "api_path": Use(str),
|
|
58
|
+
# "logging": LOGGING_VALIDATORS,
|
|
59
|
+
# "encryption": {"enabled": bool, Optional("private_key"): Use(str)},
|
|
60
|
+
# Optional("node_extra_env"): dict,
|
|
61
|
+
# Optional("node_extra_mounts"): [str],
|
|
62
|
+
# Optional("node_extra_hosts"): dict,
|
|
63
|
+
# Optional("share_algorithm_logs"): Use(bool),
|
|
64
|
+
# }
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
|
|
@@ -93,6 +100,12 @@ class NodeConfigurationManager(ConfigurationManager):
|
|
|
93
100
|
"""
|
|
94
101
|
return super().from_file(path, conf_class=NodeConfiguration)
|
|
95
102
|
|
|
103
|
+
def get_config_template(self) -> str:
|
|
104
|
+
"""
|
|
105
|
+
Get the configuration template for the node.
|
|
106
|
+
"""
|
|
107
|
+
return super()._get_config_template(NODE_TEMPLATE_FILE)
|
|
108
|
+
|
|
96
109
|
|
|
97
110
|
class ServerConfigurationManager(ConfigurationManager):
|
|
98
111
|
"""
|
|
@@ -1,22 +1,15 @@
|
|
|
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,
|
|
22
15
|
NodeConfigurationManager,
|
|
@@ -25,266 +18,7 @@ from vantage6.cli.configuration_manager import (
|
|
|
25
18
|
from vantage6.cli.context import select_context_class
|
|
26
19
|
|
|
27
20
|
|
|
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(
|
|
21
|
+
def add_common_server_config(
|
|
288
22
|
config: dict, instance_type: InstanceType, instance_name: str
|
|
289
23
|
) -> dict:
|
|
290
24
|
"""
|
|
@@ -364,60 +98,6 @@ def _add_common_server_config(
|
|
|
364
98
|
return config, is_production
|
|
365
99
|
|
|
366
100
|
|
|
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
101
|
def _add_production_server_config(config: dict) -> dict:
|
|
422
102
|
"""
|
|
423
103
|
Add the production server configuration to the config
|
|
@@ -445,75 +125,8 @@ def _add_production_server_config(config: dict) -> dict:
|
|
|
445
125
|
return config
|
|
446
126
|
|
|
447
127
|
|
|
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
128
|
def configuration_wizard(
|
|
129
|
+
questionnaire_function: callable,
|
|
517
130
|
type_: InstanceType,
|
|
518
131
|
instance_name: str,
|
|
519
132
|
system_folders: bool,
|
|
@@ -523,6 +136,8 @@ def configuration_wizard(
|
|
|
523
136
|
|
|
524
137
|
Parameters
|
|
525
138
|
----------
|
|
139
|
+
questionnaire_function : callable
|
|
140
|
+
Function to generate the configuration
|
|
526
141
|
type_ : InstanceType
|
|
527
142
|
Type of the instance to create a configuration for
|
|
528
143
|
instance_name : str
|
|
@@ -540,19 +155,20 @@ def configuration_wizard(
|
|
|
540
155
|
|
|
541
156
|
# invoke questionaire to create configuration file
|
|
542
157
|
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)
|
|
158
|
+
config = questionnaire_function(dirs, instance_name)
|
|
548
159
|
else:
|
|
549
|
-
|
|
550
|
-
config = algo_store_configuration_questionaire(instance_name)
|
|
160
|
+
config = questionnaire_function(instance_name)
|
|
551
161
|
|
|
552
162
|
# in the case of an environment we need to add it to the current
|
|
553
163
|
# configuration. In the case of application we can simply overwrite this
|
|
554
164
|
# key (although there might be environments present)
|
|
555
165
|
config_file = Path(dirs.get("config")) / (instance_name + ".yaml")
|
|
166
|
+
if type_ == InstanceType.NODE:
|
|
167
|
+
conf_manager = NodeConfigurationManager
|
|
168
|
+
elif type_ == InstanceType.SERVER:
|
|
169
|
+
conf_manager = ServerConfigurationManager
|
|
170
|
+
else:
|
|
171
|
+
conf_manager = AlgorithmStoreConfigurationManager
|
|
556
172
|
|
|
557
173
|
if Path(config_file).exists():
|
|
558
174
|
config_manager = conf_manager.from_file(config_file)
|
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]
|
vantage6/cli/globals.py
CHANGED
|
@@ -85,3 +85,23 @@ class ChartName(StrEnumBase):
|
|
|
85
85
|
ALGORITHM_STORE = "store"
|
|
86
86
|
NODE = "node"
|
|
87
87
|
AUTH = "auth"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class CLICommandName(StrEnumBase):
|
|
91
|
+
"""Enum containing CLI command names"""
|
|
92
|
+
|
|
93
|
+
SERVER = "server"
|
|
94
|
+
ALGORITHM_STORE = "algorithm-store"
|
|
95
|
+
NODE = "node"
|
|
96
|
+
ALGORITHM = "algorithm"
|
|
97
|
+
TEST = "test"
|
|
98
|
+
DEV = "dev"
|
|
99
|
+
USE = "use"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class InfraComponentName(StrEnumBase):
|
|
103
|
+
"""Enum containing infrastructure components"""
|
|
104
|
+
|
|
105
|
+
SERVER = "server"
|
|
106
|
+
ALGORITHM_STORE = "store"
|
|
107
|
+
NODE = "node"
|