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.

Files changed (69) hide show
  1. vantage6/cli/algorithm/generate_algorithm_json.py +9 -9
  2. vantage6/cli/algorithm/update.py +1 -1
  3. vantage6/cli/algostore/attach.py +1 -0
  4. vantage6/cli/algostore/files.py +3 -2
  5. vantage6/cli/algostore/list.py +0 -3
  6. vantage6/cli/algostore/new.py +83 -2
  7. vantage6/cli/algostore/remove.py +18 -34
  8. vantage6/cli/algostore/start.py +10 -7
  9. vantage6/cli/algostore/stop.py +12 -50
  10. vantage6/cli/auth/attach.py +60 -0
  11. vantage6/cli/auth/files.py +16 -0
  12. vantage6/cli/auth/list.py +13 -0
  13. vantage6/cli/auth/new.py +80 -0
  14. vantage6/cli/auth/remove.py +31 -0
  15. vantage6/cli/auth/start.py +80 -0
  16. vantage6/cli/auth/stop.py +64 -0
  17. vantage6/cli/cli.py +67 -37
  18. vantage6/cli/common/new.py +28 -3
  19. vantage6/cli/common/remove.py +54 -0
  20. vantage6/cli/common/start.py +31 -2
  21. vantage6/cli/common/stop.py +79 -1
  22. vantage6/cli/common/utils.py +47 -4
  23. vantage6/cli/configuration_manager.py +57 -13
  24. vantage6/cli/configuration_wizard.py +18 -397
  25. vantage6/cli/context/__init__.py +3 -0
  26. vantage6/cli/context/auth.py +107 -0
  27. vantage6/cli/context/base_server.py +0 -4
  28. vantage6/cli/context/node.py +10 -17
  29. vantage6/cli/dev/clean.py +28 -0
  30. vantage6/cli/dev/common.py +34 -0
  31. vantage6/cli/dev/rebuild.py +39 -0
  32. vantage6/cli/dev/start.py +36 -0
  33. vantage6/cli/dev/stop.py +23 -0
  34. vantage6/cli/globals.py +24 -1
  35. vantage6/cli/node/attach.py +1 -0
  36. vantage6/cli/node/files.py +12 -25
  37. vantage6/cli/node/list.py +5 -4
  38. vantage6/cli/node/new.py +348 -28
  39. vantage6/cli/node/remove.py +14 -90
  40. vantage6/cli/node/restart.py +30 -51
  41. vantage6/cli/node/start.py +81 -304
  42. vantage6/cli/node/stop.py +36 -96
  43. vantage6/cli/node/version.py +5 -4
  44. vantage6/cli/prometheus/monitoring_manager.py +5 -3
  45. vantage6/cli/rabbitmq/queue_manager.py +13 -11
  46. vantage6/cli/server/attach.py +1 -0
  47. vantage6/cli/server/common/__init__.py +1 -27
  48. vantage6/cli/server/import_.py +1 -1
  49. vantage6/cli/server/new.py +83 -2
  50. vantage6/cli/server/remove.py +12 -33
  51. vantage6/cli/server/start.py +8 -6
  52. vantage6/cli/server/stop.py +10 -39
  53. vantage6/cli/template/algo_store_config.j2 +1 -1
  54. vantage6/cli/template/auth_config.j2 +230 -0
  55. vantage6/cli/template/node_config.j2 +336 -33
  56. vantage6/cli/template/node_config_nonk8s.j2 +33 -0
  57. vantage6/cli/test/common/diagnostic_runner.py +5 -3
  58. vantage6/cli/use/namespace.py +2 -1
  59. vantage6/cli/utils.py +0 -2
  60. {vantage6-5.0.0a34.dist-info → vantage6-5.0.0a36.dist-info}/METADATA +3 -3
  61. vantage6-5.0.0a36.dist-info/RECORD +86 -0
  62. vantage6/cli/dev/create.py +0 -693
  63. vantage6/cli/dev/data/km_dataset.csv +0 -2401
  64. vantage6/cli/dev/remove.py +0 -112
  65. vantage6/cli/node/clean.py +0 -46
  66. vantage6/cli/server/shell.py +0 -54
  67. vantage6-5.0.0a34.dist-info/RECORD +0 -75
  68. {vantage6-5.0.0a34.dist-info → vantage6-5.0.0a36.dist-info}/WHEEL +0 -0
  69. {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, warning
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 node_configuration_questionaire(dirs: dict, instance_name: str) -> dict:
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
- conf_manager = NodeConfigurationManager
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
- conf_manager = AlgorithmStoreConfigurationManager
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)
@@ -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)
@@ -1,9 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import os.path
4
-
5
- from sqlalchemy.engine.url import make_url
6
-
7
3
  from vantage6.common.context import AppContext
8
4
 
9
5
  from vantage6.cli.globals import (
@@ -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
- return self.config["databases"]
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
- return hashlib.sha256(
256
- os.environ.get("V6_API_KEY").encode(STRING_ENCODING)
257
- ).hexdigest()[:16]
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)