vantage6 5.0.0a33__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.

Files changed (45) hide show
  1. vantage6/cli/algostore/new.py +106 -47
  2. vantage6/cli/algostore/remove.py +18 -34
  3. vantage6/cli/algostore/start.py +36 -67
  4. vantage6/cli/algostore/stop.py +43 -46
  5. vantage6/cli/cli.py +31 -33
  6. vantage6/cli/common/new.py +85 -0
  7. vantage6/cli/common/remove.py +54 -0
  8. vantage6/cli/common/start.py +36 -213
  9. vantage6/cli/common/stop.py +78 -0
  10. vantage6/cli/common/utils.py +253 -16
  11. vantage6/cli/configuration_manager.py +90 -12
  12. vantage6/cli/configuration_wizard.py +49 -414
  13. vantage6/cli/context/algorithm_store.py +7 -6
  14. vantage6/cli/context/base_server.py +22 -30
  15. vantage6/cli/context/node.py +14 -17
  16. vantage6/cli/context/server.py +16 -7
  17. vantage6/cli/globals.py +29 -8
  18. vantage6/cli/node/attach.py +1 -0
  19. vantage6/cli/node/common/__init__.py +1 -1
  20. vantage6/cli/node/create_private_key.py +9 -6
  21. vantage6/cli/node/files.py +12 -25
  22. vantage6/cli/node/new.py +348 -28
  23. vantage6/cli/node/remove.py +14 -90
  24. vantage6/cli/node/restart.py +30 -51
  25. vantage6/cli/node/set_api_key.py +7 -4
  26. vantage6/cli/node/start.py +81 -304
  27. vantage6/cli/node/stop.py +36 -96
  28. vantage6/cli/server/import_.py +1 -2
  29. vantage6/cli/server/list.py +0 -3
  30. vantage6/cli/server/new.py +72 -42
  31. vantage6/cli/server/remove.py +12 -33
  32. vantage6/cli/server/shell.py +1 -1
  33. vantage6/cli/server/start.py +22 -20
  34. vantage6/cli/server/stop.py +37 -17
  35. vantage6/cli/template/algo_store_config.j2 +195 -22
  36. vantage6/cli/template/node_config.j2 +336 -33
  37. vantage6/cli/template/server_config.j2 +255 -33
  38. vantage6/cli/utils.py +0 -2
  39. {vantage6-5.0.0a33.dist-info → vantage6-5.0.0a35.dist-info}/METADATA +4 -4
  40. vantage6-5.0.0a35.dist-info/RECORD +75 -0
  41. vantage6/cli/node/clean.py +0 -46
  42. vantage6/cli/template/server_import_config.j2 +0 -31
  43. vantage6-5.0.0a33.dist-info/RECORD +0 -75
  44. {vantage6-5.0.0a33.dist-info → vantage6-5.0.0a35.dist-info}/WHEEL +0 -0
  45. {vantage6-5.0.0a33.dist-info → vantage6-5.0.0a35.dist-info}/entry_points.txt +0 -0
vantage6/cli/node/new.py CHANGED
@@ -1,13 +1,22 @@
1
+ import os
2
+ from pathlib import Path
3
+
1
4
  import click
2
- from colorama import Fore, Style
5
+ import questionary as q
3
6
 
4
- from vantage6.common import ensure_config_dir_writable, error, info
5
- from vantage6.common.globals import InstanceType
7
+ from vantage6.common import error, info, warning
8
+ from vantage6.common.client.node_client import NodeClient
9
+ from vantage6.common.globals import (
10
+ FILE_BASED_DATABASE_TYPES,
11
+ SERVICE_BASED_DATABASE_TYPES,
12
+ InstanceType,
13
+ NodePolicy,
14
+ Ports,
15
+ RequiredNodeEnvVars,
16
+ )
6
17
 
7
- from vantage6.cli.configuration_wizard import configuration_wizard
8
- from vantage6.cli.context.node import NodeContext
18
+ from vantage6.cli.common.new import new
9
19
  from vantage6.cli.globals import DEFAULT_NODE_SYSTEM_FOLDERS as N_FOL
10
- from vantage6.cli.utils import check_config_name_allowed, prompt_config_name
11
20
 
12
21
 
13
22
  @click.command()
@@ -23,38 +32,349 @@ from vantage6.cli.utils import check_config_name_allowed, prompt_config_name
23
32
  "system_folders",
24
33
  flag_value=False,
25
34
  default=N_FOL,
26
- help="Store this configuration in the user folders. This is the " "default",
35
+ help="Store this configuration in the user folders. This is the default.",
36
+ )
37
+ @click.option("--context", default=None, help="Kubernetes context to use")
38
+ @click.option(
39
+ "--namespace",
40
+ default=None,
41
+ help="Kubernetes namespace to use",
27
42
  )
28
- def cli_node_new_configuration(name: str, system_folders: bool) -> None:
43
+ def cli_node_new_configuration(
44
+ name: str,
45
+ system_folders: bool,
46
+ namespace: str,
47
+ context: str,
48
+ ) -> None:
29
49
  """
30
50
  Create a new node configuration.
31
51
 
32
52
  Checks if the configuration already exists. If this is not the case
33
53
  a questionnaire is invoked to create a new configuration file.
34
54
  """
35
- name = prompt_config_name(name)
36
- # check if config name is allowed docker name
37
- check_config_name_allowed(name)
55
+ new(
56
+ questionnaire_function=node_configuration_questionaire,
57
+ name=name,
58
+ system_folders=system_folders,
59
+ namespace=namespace,
60
+ context=context,
61
+ type_=InstanceType.NODE,
62
+ )
63
+
64
+
65
+ def node_configuration_questionaire(dirs: dict, instance_name: str) -> dict:
66
+ """
67
+ Questionary to generate a config file for the node instance.
68
+
69
+ Parameters
70
+ ----------
71
+ dirs : dict
72
+ Dictionary with the directories of the node instance.
73
+ instance_name : str
74
+ Name of the node instance.
75
+
76
+ Returns
77
+ -------
78
+ dict
79
+ Dictionary with the new node configuration
80
+ """
81
+ config = q.unsafe_prompt(
82
+ [
83
+ {"type": "text", "name": "api_key", "message": "Enter given api-key:"},
84
+ {
85
+ "type": "text",
86
+ "name": "server_url",
87
+ "message": "The base-URL of the server:",
88
+ "default": "http://localhost",
89
+ },
90
+ ]
91
+ )
92
+ # remove trailing slash from server_url if entered by user
93
+ config["server_url"] = config["server_url"].rstrip("/")
94
+
95
+ # set default port to the https port if server_url is https
96
+ default_port = (
97
+ str(Ports.HTTPS)
98
+ if config["server_url"].startswith("https")
99
+ else str(Ports.DEV_SERVER)
100
+ )
101
+
102
+ config = config | q.unsafe_prompt(
103
+ [
104
+ {
105
+ "type": "text",
106
+ "name": "port",
107
+ "message": "Enter port to which the server listens:",
108
+ "default": default_port,
109
+ },
110
+ {
111
+ "type": "text",
112
+ "name": "api_path",
113
+ "message": "Path of the api:",
114
+ "default": "/api",
115
+ },
116
+ {
117
+ "type": "text",
118
+ "name": "task_dir",
119
+ "message": "Task directory path:",
120
+ "default": str(dirs["data"]),
121
+ },
122
+ ]
123
+ )
124
+
125
+ config["databases"] = {"fileBased": [], "serviceBased": []}
126
+ while q.confirm("Do you want to add a database?").unsafe_ask():
127
+ db_label = q.select(
128
+ "What type of database do you want to add?",
129
+ choices=["File database", "Database reachable by URI"],
130
+ ).unsafe_ask()
131
+
132
+ if db_label == "File database":
133
+ config["databases"]["fileBased"].append(_get_file_based_database_config())
134
+ else:
135
+ config["databases"]["serviceBased"].append(
136
+ _get_service_based_database_config()
137
+ )
138
+
139
+ is_policies = q.confirm(
140
+ "Do you want to limit the algorithms allowed to run on your node? This "
141
+ "should always be done for production scenarios.",
142
+ default=True,
143
+ ).unsafe_ask()
144
+ policies = {}
145
+ if is_policies:
146
+ info(
147
+ "You can limit the algorithms that can run on your node in two ways: by "
148
+ "allowing specific algorithms or by allowing all algorithms in a given "
149
+ "algorithm store."
150
+ )
151
+ ask_single_algorithms = q.confirm(
152
+ "Do you want to enter a list of allowed algorithms?"
153
+ ).unsafe_ask()
154
+ if ask_single_algorithms:
155
+ policies[NodePolicy.ALLOWED_ALGORITHMS.value] = _get_allowed_algorithms()
156
+ ask_algorithm_stores = q.confirm(
157
+ "Do you want to allow algorithms from specific algorithm stores?"
158
+ ).unsafe_ask()
159
+ if ask_algorithm_stores:
160
+ policies[NodePolicy.ALLOWED_ALGORITHM_STORES.value] = (
161
+ _get_allowed_algorithm_stores()
162
+ )
163
+ if ask_single_algorithms and ask_algorithm_stores:
164
+ require_both_whitelists = q.confirm(
165
+ "Do you want to allow only algorithms that are both in the list of "
166
+ "allowed algorithms *AND* are part of one of the allowed algorithm "
167
+ "stores? If not, algorithms will be allowed if they are in either the "
168
+ "list of allowed algorithms or one of the allowed algorithm stores.",
169
+ default=True,
170
+ ).unsafe_ask()
171
+ policies["allow_either_whitelist_or_store"] = not require_both_whitelists
172
+ if policies:
173
+ config["policies"] = policies
38
174
 
39
- # check that this config does not exist
40
- if NodeContext.config_exists(name, system_folders):
41
- error(f"Configuration {name} already exists!")
42
- exit(1)
175
+ res = q.select(
176
+ "Which level of logging would you like?",
177
+ choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "NOTSET"],
178
+ ).unsafe_ask()
43
179
 
44
- # Check that we can write in this folder
45
- if not ensure_config_dir_writable(system_folders):
46
- error("Cannot write configuration file. Exiting...")
47
- exit(1)
180
+ config["logging"] = {
181
+ "level": res,
182
+ "use_console": True,
183
+ "backup_count": 5,
184
+ "max_size": 1024,
185
+ "format": "%(asctime)s - %(name)-14s - %(levelname)-8s - %(message)s",
186
+ "datefmt": "%Y-%m-%d %H:%M:%S",
187
+ "loggers": [
188
+ {"name": "urllib3", "level": "warning"},
189
+ {"name": "requests", "level": "warning"},
190
+ {"name": "engineio.client", "level": "warning"},
191
+ {"name": "docker.utils.config", "level": "warning"},
192
+ {"name": "docker.auth", "level": "warning"},
193
+ ],
194
+ }
48
195
 
49
- # create config in ctx location
50
- flag = "--system" if system_folders else ""
196
+ # Check if we can login to the server to retrieve collaboration settings
197
+ client = NodeClient(
198
+ instance_name,
199
+ config["api_key"],
200
+ server_url=f"{config['server_url']}:{config['port']}{config['api_path']}",
201
+ auth_url=os.environ.get(RequiredNodeEnvVars.KEYCLOAK_URL.value),
202
+ )
51
203
  try:
52
- cfg_file = configuration_wizard(InstanceType.NODE, name, system_folders)
53
- except KeyboardInterrupt:
54
- error("Configuration creation aborted.")
55
- exit(1)
56
- info(f"New configuration created: {Fore.GREEN}{cfg_file}{Style.RESET_ALL}")
204
+ client.authenticate()
205
+ except Exception as e:
206
+ error(f"Could not authenticate with server: {e}")
207
+ error("Please check (1) your API key and (2) if your server is online")
208
+ warning(
209
+ "If you continue, you should provide your collaboration settings manually."
210
+ )
211
+ if q.confirm("Do you want to abort?", default=True).unsafe_ask():
212
+ exit(0)
213
+
214
+ if client.whoami is not None:
215
+ encryption = client.is_encrypted_collaboration()
216
+ # TODO when we build collaboration policies, update this to provide
217
+ # the node admin with a list of all policies, and whether or not
218
+ # to accept them
219
+ q.confirm(
220
+ f"Encryption is {'enabled' if encryption else 'disabled'}"
221
+ f" for this collaboration. Accept?",
222
+ default=True,
223
+ ).unsafe_ask()
224
+ else:
225
+ encryption = q.confirm("Enable encryption?", default=True).unsafe_ask()
226
+
227
+ private_key = (
228
+ "" if not encryption else q.text("Path to private key file:").unsafe_ask()
229
+ )
230
+
231
+ config["encryption"] = {
232
+ "enabled": encryption is True or encryption == "true",
233
+ "private_key": private_key,
234
+ }
235
+
236
+ # pack the entire config in a dict with the 'node' key at top level
237
+ return {"node": config}
238
+
239
+
240
+ def _get_file_based_database_config() -> dict:
241
+ """
242
+ Prompt the user for the file-based database configuration
243
+ """
244
+ db_label = _get_database_label()
245
+ while True:
246
+ db_path = q.text(
247
+ "Path to the database file:",
248
+ ).unsafe_ask()
249
+ if Path(db_path).exists():
250
+ break
251
+ else:
252
+ error("The path to the database file does not exist. Please try again.")
253
+ db_path_resolved = Path(db_path).resolve()
254
+ db_dir = db_path_resolved.parent
255
+ db_filename = db_path_resolved.name
256
+ db_type = q.select("Database type:", choices=FILE_BASED_DATABASE_TYPES).unsafe_ask()
257
+ return {
258
+ "name": db_label,
259
+ "uri": db_path,
260
+ "type": db_type,
261
+ "volumePath": db_dir,
262
+ "originalName": db_filename,
263
+ }
264
+
265
+
266
+ def _get_service_based_database_config() -> dict:
267
+ """
268
+ Prompt the user for the service-based database configuration
269
+
270
+ Returns
271
+ -------
272
+ dict
273
+ Dictionary with the service-based database configuration
274
+ """
275
+ db_label = _get_database_label()
276
+ db_uri = q.text(
277
+ "Database URI:",
278
+ ).unsafe_ask()
279
+ db_type = q.select(
280
+ "Database type:", choices=SERVICE_BASED_DATABASE_TYPES
281
+ ).unsafe_ask()
282
+
283
+ env_vars = {}
284
+ info("You can add environment variables to the database configuration.")
285
+ info("These variables will be available to the algorithms on your node.")
286
+ info("Example: MY_POSTGRES_USER=vantage6, MY_POSTGRES_PASSWORD=vantage6")
287
+ while q.confirm("Do you want to add an environment variable?").unsafe_ask():
288
+ env_var_name = q.text(
289
+ "Enter the name of the environment variable:"
290
+ ).unsafe_ask()
291
+ env_var_value = q.text(
292
+ "Enter the value of the environment variable:"
293
+ ).unsafe_ask()
294
+ env_vars[env_var_name] = env_var_value
295
+
296
+ return {
297
+ "name": db_label,
298
+ "uri": db_uri,
299
+ "type": db_type,
300
+ "env": env_vars,
301
+ }
302
+
303
+
304
+ def _get_database_label() -> str:
305
+ """
306
+ Prompt the user for the label of the database
307
+ """
308
+ return q.text(
309
+ "Enter unique label for the database:",
310
+ default="default",
311
+ ).unsafe_ask()
312
+
313
+
314
+ def _get_allowed_algorithms() -> list[str]:
315
+ """
316
+ Prompt the user for the allowed algorithms on their node
317
+
318
+ Returns
319
+ -------
320
+ list[str]
321
+ List of allowed algorithms or regular expressions to match them
322
+ """
323
+ info("Below you can add algorithms that are allowed to run on your node.")
324
+ info(
325
+ "You can use regular expressions to match multiple algorithms, or you can "
326
+ "use strings to provide one algorithm at a time."
327
+ )
328
+ info("Examples:")
329
+ info(r"^harbor2\.vantage6\.ai/demo/average$ Allow the demo average algorithm")
330
+ info(
331
+ r"^harbor2\.vantage6\.ai/algorithms/.* Allow all algorithms from "
332
+ "harbor2.vantage6.ai/algorithms"
333
+ )
334
+ info(
335
+ r"^harbor2\.vantage6\.ai/demo/average@sha256:82becede...$ Allow a "
336
+ "specific hash of average algorithm"
337
+ )
338
+ allowed_algorithms = []
339
+ while True:
340
+ algo = q.text(message="Enter your algorithm expression:").unsafe_ask()
341
+ allowed_algorithms.append(algo)
342
+ if not q.confirm(
343
+ "Do you want to add another algorithm expression?", default=True
344
+ ).unsafe_ask():
345
+ break
346
+ return allowed_algorithms
347
+
348
+
349
+ def _get_allowed_algorithm_stores() -> list[str]:
350
+ """
351
+ Prompt the user for the allowed algorithm stores on their node
352
+
353
+ Returns
354
+ -------
355
+ list[str]
356
+ List of allowed algorithm stores
357
+ """
358
+ info("Below you can add algorithm stores that are allowed to run on your node.")
359
+ info(
360
+ "You can use regular expressions to match multiple algorithm stores, or you can"
361
+ " use strings to provide one algorithm store at a time."
362
+ )
363
+ info("Examples:")
364
+ info(
365
+ "https://store.cotopaxi.vantage6.ai Allow all algorithms from the "
366
+ "community store"
367
+ )
57
368
  info(
58
- f"You can start the node by running "
59
- f"{Fore.GREEN}v6 node start {flag}{Style.RESET_ALL}"
369
+ r"^https://*\.vantage6\.ai$ Allow all algorithms from any "
370
+ "store hosted on vantage6.ai"
60
371
  )
372
+ allowed_algorithm_stores = []
373
+ while True:
374
+ store = q.text(message="Enter the URL of the algorithm store:").unsafe_ask()
375
+ allowed_algorithm_stores.append(store)
376
+ if not q.confirm(
377
+ "Do you want to add another algorithm store?", default=True
378
+ ).unsafe_ask():
379
+ break
380
+ return allowed_algorithm_stores
@@ -1,112 +1,36 @@
1
- import click
2
- import questionary as q
3
- import docker
4
- import os.path
5
- import itertools
6
-
7
1
  from pathlib import Path
8
2
  from shutil import rmtree
9
3
 
10
- from vantage6.common import (
11
- error,
12
- info,
13
- debug,
14
- )
15
- from vantage6.common.globals import APPNAME
4
+ import click
16
5
 
6
+ from vantage6.common.globals import InstanceType
17
7
 
8
+ from vantage6.cli.common.decorator import click_insert_context
9
+ from vantage6.cli.common.remove import execute_remove
18
10
  from vantage6.cli.context.node import NodeContext
19
- from vantage6.cli.globals import DEFAULT_NODE_SYSTEM_FOLDERS as N_FOL
20
- from vantage6.cli.utils import check_if_docker_daemon_is_running, remove_file
21
- from vantage6.cli.node.common import select_node, find_running_node_names
22
-
23
- # TODO v5+ remove this - just a dummy to prevent import issues from v4 CLI
24
- # from vantage6.common.globals import VPN_CONFIG_FILE
25
- VPN_CONFIG_FILE = "vpn.conf"
11
+ from vantage6.cli.globals import InfraComponentName
26
12
 
27
13
 
28
14
  @click.command()
29
- @click.option("-n", "--name", default=None, help="Configuration name")
30
- @click.option(
31
- "--system",
32
- "system_folders",
33
- flag_value=True,
34
- help="Search for configuration in system folders rather than " "user folders",
35
- )
36
- @click.option(
37
- "--user",
38
- "system_folders",
39
- flag_value=False,
40
- default=N_FOL,
41
- help="Search for configuration in user folders rather than "
42
- "system folders. This is the default",
43
- )
44
15
  @click.option(
45
16
  "-f", "--force", type=bool, flag_value=True, help="Don't ask for confirmation"
46
17
  )
47
- def cli_node_remove(name: str, system_folders: bool, force: bool) -> None:
18
+ @click_insert_context(
19
+ type_=InstanceType.NODE, include_name=True, include_system_folders=True
20
+ )
21
+ def cli_node_remove(
22
+ ctx: NodeContext, name: str, system_folders: bool, force: bool
23
+ ) -> None:
48
24
  """
49
25
  Delete a node permanently.
50
26
 
51
27
  Remove the configuration file, log file, and docker volumes attached to
52
28
  the node.
53
29
  """
54
- # select configuration name if none supplied
55
- name = select_node(name, system_folders)
56
-
57
- client = docker.from_env()
58
- check_if_docker_daemon_is_running(client)
59
-
60
- # check if node is still running, otherwise don't allow deleting it
61
- running_node_names = find_running_node_names(client)
62
-
63
- post_fix = "system" if system_folders else "user"
64
- node_container_name = f"{APPNAME}-{name}-{post_fix}"
65
- if node_container_name in running_node_names:
66
- error(
67
- f"Node {name} is still running! Please stop the node before " "deleting it."
68
- )
69
- exit(1)
70
-
71
- if not force:
72
- if not q.confirm(
73
- "This node will be deleted permanently including its "
74
- "configuration. Are you sure?",
75
- default=False,
76
- ).ask():
77
- info("Node will not be deleted")
78
- exit(0)
79
-
80
- # create node context
81
- ctx = NodeContext(name, system_folders=system_folders)
82
-
83
- # remove the docker volume and any temporary volumes
84
- debug("Deleting docker volumes")
85
- volumes = client.volumes.list()
86
- for vol in volumes:
87
- if vol.name.startswith(ctx.docker_volume_name): # includes tmp volumes
88
- info(f"Deleting docker volume {vol.name}")
89
- vol.remove()
90
- # remove docker vpn volume
91
- if vol.name == ctx.docker_vpn_volume_name:
92
- info(f"Deleting VPN docker volume {vol.name}")
93
- vol.remove()
94
-
95
- # remove the VPN configuration file
96
- vpn_config_file = os.path.join(ctx.data_dir, "vpn", VPN_CONFIG_FILE)
97
- remove_file(vpn_config_file, "VPN configuration")
98
-
99
- # remove the config file
100
- remove_file(ctx.config_file, "configuration")
101
30
 
102
- # remove the log file. As this process opens the log file above, the log
103
- # handlers need to be closed before deleting
104
- log_dir = Path(ctx.log_file.parent)
105
- info(f"Removing log file {log_dir}")
106
- for handler in itertools.chain(ctx.log.handlers, ctx.log.root.handlers):
107
- handler.close()
108
- # remove the whole folder with all the log files
109
- rmtree(log_dir)
31
+ execute_remove(
32
+ ctx, InstanceType.NODE, InfraComponentName.NODE, name, system_folders, force
33
+ )
110
34
 
111
35
  # remove the folder: if it hasn't been started yet this won't exist...
112
36
  if Path.exists(ctx.config_dir / name):
@@ -1,23 +1,29 @@
1
1
  import subprocess
2
+
2
3
  import click
3
4
  import questionary as q
4
- import docker
5
5
 
6
- from vantage6.common import warning, error
7
- from vantage6.common.docker.addons import check_docker_running
8
- from vantage6.cli.common.utils import get_name_from_container_name
9
- from vantage6.cli.node.stop import cli_node_stop
10
- from vantage6.cli.node.common import find_running_node_names
6
+ from vantage6.common import error, warning
7
+ from vantage6.common.globals import InstanceType
8
+
9
+ from vantage6.cli.common.utils import (
10
+ find_running_service_names,
11
+ get_config_name_from_service_name,
12
+ select_context_and_namespace,
13
+ )
11
14
  from vantage6.cli.globals import DEFAULT_NODE_SYSTEM_FOLDERS as N_FOL
15
+ from vantage6.cli.node.stop import cli_node_stop
12
16
 
13
17
 
14
18
  @click.command()
15
19
  @click.option("-n", "--name", default=None, help="Configuration name")
20
+ @click.option("--context", default=None, help="Kubernetes context to use")
21
+ @click.option("--namespace", default=None, help="Kubernetes namespace to use")
16
22
  @click.option(
17
23
  "--system",
18
24
  "system_folders",
19
25
  flag_value=True,
20
- help="Search for configuration in system folders instead of " "user folders",
26
+ help="Search for configuration in system folders instead of user folders",
21
27
  )
22
28
  @click.option(
23
29
  "--user",
@@ -27,53 +33,35 @@ from vantage6.cli.globals import DEFAULT_NODE_SYSTEM_FOLDERS as N_FOL
27
33
  help="Search for configuration in the user folders instead of "
28
34
  "system folders. This is the default.",
29
35
  )
30
- @click.option("-i", "--image", default=None, help="Node Docker image to use")
31
- @click.option(
32
- "--keep/--auto-remove",
33
- default=False,
34
- help="Keep node container after finishing. Useful for debugging",
35
- )
36
- @click.option(
37
- "--force-db-mount",
38
- is_flag=True,
39
- help="Always mount node databases; skip the check if they are existing files.",
40
- )
41
36
  @click.option(
42
37
  "--attach/--detach",
43
38
  default=False,
44
- help="Show node logs on the current console after starting the " "node",
45
- )
46
- @click.option(
47
- "--mount-src",
48
- default="",
49
- help="Override vantage6 source code in container with the source"
50
- " code in this path",
39
+ help="Show node logs on the current console after starting the node",
51
40
  )
52
41
  @click.option("--all", "all_nodes", flag_value=True, help="Stop all running nodes")
53
- @click.option(
54
- "--force",
55
- "force",
56
- flag_value=True,
57
- help="Kill nodes instantly; don't wait for them to shut down",
58
- )
59
42
  @click.pass_context
60
43
  def cli_node_restart(
61
44
  click_ctx: click.Context,
62
45
  name: str,
46
+ context: str,
47
+ namespace: str,
63
48
  system_folders: bool,
64
- image: str,
65
- keep: bool,
66
- mount_src: str,
67
49
  attach: bool,
68
- force_db_mount: bool,
69
50
  all_nodes: bool,
70
- force: bool,
71
51
  ) -> None:
72
52
  """Restart the node"""
73
- check_docker_running()
74
- client = docker.from_env()
53
+ context, namespace = select_context_and_namespace(
54
+ context=context,
55
+ namespace=namespace,
56
+ )
75
57
 
76
- running_node_names = find_running_node_names(client)
58
+ running_node_names = find_running_service_names(
59
+ instance_type=InstanceType.NODE,
60
+ only_system_folders=system_folders,
61
+ only_user_folders=not system_folders,
62
+ context=context,
63
+ namespace=namespace,
64
+ )
77
65
  if not running_node_names:
78
66
  warning("No nodes are currently running. No action taken.")
79
67
  return
@@ -87,19 +75,19 @@ def cli_node_restart(
87
75
 
88
76
  if all_nodes:
89
77
  names = [
90
- get_name_from_container_name(container_name)
78
+ get_config_name_from_service_name(container_name)
91
79
  for container_name in running_node_names
92
80
  ]
93
81
  else:
94
82
  if not name:
95
83
  try:
96
- container_name = q.select(
84
+ helm_name = q.select(
97
85
  "Select the node you wish to restart:", choices=running_node_names
98
86
  ).unsafe_ask()
99
87
  except KeyboardInterrupt:
100
88
  error("Aborted by user!")
101
89
  return
102
- names = [get_name_from_container_name(container_name)]
90
+ names = [get_config_name_from_service_name(helm_name)]
103
91
  else:
104
92
  names = [name]
105
93
 
@@ -109,20 +97,11 @@ def cli_node_restart(
109
97
  name=node_name,
110
98
  system_folders=system_folders,
111
99
  all_nodes=False,
112
- force=force,
113
100
  )
114
101
 
115
102
  cmd = ["v6", "node", "start", "--name", node_name]
116
103
  if system_folders:
117
104
  cmd.append("--system")
118
- if image:
119
- cmd.extend(["--image", image])
120
- if keep:
121
- cmd.append("--keep")
122
- if mount_src:
123
- cmd.extend(["--mount-src", mount_src])
124
105
  if attach:
125
106
  cmd.append("--attach")
126
- if force_db_mount:
127
- cmd.append("--force-db-mount")
128
107
  subprocess.run(cmd, check=True)