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
@@ -0,0 +1,85 @@
1
+ from colorama import Fore, Style
2
+
3
+ from vantage6.common import ensure_config_dir_writable, error, info
4
+ from vantage6.common.globals import InstanceType
5
+
6
+ from vantage6.cli.common.utils import get_main_cli_command_name
7
+ from vantage6.cli.config import CliConfig
8
+ from vantage6.cli.configuration_wizard import configuration_wizard
9
+ from vantage6.cli.context import select_context_class
10
+ from vantage6.cli.utils import check_config_name_allowed, prompt_config_name
11
+
12
+
13
+ def new(
14
+ questionnaire_function: callable,
15
+ name: str,
16
+ system_folders: bool,
17
+ namespace: str,
18
+ context: str,
19
+ type_: InstanceType,
20
+ ) -> None:
21
+ """
22
+ Create a new configuration.
23
+
24
+ Parameters
25
+ ----------
26
+ questionnaire_function : callable
27
+ Function to generate the configuration
28
+ name : str
29
+ Name of the configuration
30
+ system_folders : bool
31
+ Whether to store the configuration in the system folders
32
+ namespace : str
33
+ Namespace to use
34
+ context : str
35
+ Context to use
36
+ type_ : InstanceType
37
+ Type of the configuration (node, server, algorithm store, etc)
38
+ """
39
+ cli_config = CliConfig()
40
+ context, namespace = cli_config.compare_changes_config(
41
+ context=context,
42
+ namespace=namespace,
43
+ )
44
+
45
+ name = prompt_config_name(name)
46
+
47
+ # check if name is valid
48
+ check_config_name_allowed(name)
49
+
50
+ # check that this config does not exist
51
+ ctx_class = select_context_class(type_)
52
+ try:
53
+ if ctx_class.config_exists(name, system_folders):
54
+ error(f"Configuration {Fore.RED}{name}{Style.RESET_ALL} already exists!")
55
+ exit(1)
56
+ except Exception as e:
57
+ error(e)
58
+ exit(1)
59
+
60
+ command_name = get_main_cli_command_name(type_)
61
+
62
+ # Check that we can write in this folder
63
+ if not ensure_config_dir_writable(system_folders):
64
+ error("Your user does not have write access to all folders. Exiting")
65
+ info(
66
+ f"Create a new {command_name} using '{Fore.GREEN}v6 {command_name} new "
67
+ f"--user{Style.RESET_ALL}' instead!"
68
+ )
69
+ exit(1)
70
+
71
+ # create config in ctx location
72
+ try:
73
+ cfg_file = configuration_wizard(
74
+ questionnaire_function, type_, name, system_folders
75
+ )
76
+ except KeyboardInterrupt:
77
+ error("Configuration creation aborted.")
78
+ exit(1)
79
+ info(f"New configuration created: {Fore.GREEN}{cfg_file}{Style.RESET_ALL}")
80
+
81
+ flag = "" if system_folders else "--user"
82
+ info(
83
+ f"You can start the {command_name} by running {Fore.GREEN}v6 {command_name} "
84
+ f"start {flag}{Style.RESET_ALL}"
85
+ )
@@ -0,0 +1,54 @@
1
+ import itertools
2
+ from pathlib import Path
3
+ from shutil import rmtree
4
+
5
+ import questionary as q
6
+
7
+ from vantage6.common import (
8
+ error,
9
+ info,
10
+ )
11
+ from vantage6.common.context import AppContext
12
+ from vantage6.common.globals import InstanceType
13
+
14
+ from vantage6.cli.common.utils import check_running
15
+ from vantage6.cli.globals import InfraComponentName
16
+ from vantage6.cli.utils import remove_file
17
+
18
+
19
+ def execute_remove(
20
+ ctx: AppContext,
21
+ instance_type: InstanceType,
22
+ infra_component: InfraComponentName,
23
+ name: str,
24
+ system_folders: bool,
25
+ force: bool,
26
+ ) -> None:
27
+ if check_running(ctx.helm_release_name, instance_type, name, system_folders):
28
+ error(
29
+ f"The {infra_component.value} {name} is still running! Please stop the "
30
+ f"{infra_component.value} before deleting it."
31
+ )
32
+ exit(1)
33
+
34
+ if not force:
35
+ if not q.confirm(
36
+ f"This {infra_component.value} will be deleted permanently including its "
37
+ "configuration. Are you sure?",
38
+ default=False,
39
+ ).ask():
40
+ info(f"The {infra_component.value} {name} will not be deleted")
41
+ exit(0)
42
+
43
+ # remove the config file
44
+ remove_file(ctx.config_file, "configuration")
45
+
46
+ # ensure log files are closed before removing
47
+ log_dir = Path(ctx.log_file.parent)
48
+ if log_dir.exists():
49
+ info(f"Removing log directory: {log_dir}")
50
+ for handler in itertools.chain(ctx.log.handlers, ctx.log.root.handlers):
51
+ handler.close()
52
+ # remove the whole folder with all the log files. This may also still contain
53
+ # other files like (for the server) RabbitMQ configuration etc
54
+ rmtree(log_dir)
@@ -1,25 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
- import enum
4
- import os
5
3
  import re
6
4
  import subprocess
7
5
  import time
8
6
  from os import PathLike
9
7
  from pathlib import Path
10
- from threading import Thread
11
8
 
12
9
  import docker
13
- from colorama import Fore, Style
14
10
  from docker.client import DockerClient
15
- from docker.models.containers import Container
16
- from sqlalchemy.engine.url import make_url
17
11
 
18
12
  from vantage6.common import error, info, warning
19
13
  from vantage6.common.context import AppContext
20
- from vantage6.common.docker.addons import check_docker_running, pull_image
14
+ from vantage6.common.docker.addons import pull_image
21
15
  from vantage6.common.globals import (
22
- APPNAME,
23
16
  DEFAULT_ALGO_STORE_IMAGE,
24
17
  DEFAULT_CHART_REPO,
25
18
  DEFAULT_DOCKER_REGISTRY,
@@ -29,83 +22,36 @@ from vantage6.common.globals import (
29
22
  InstanceType,
30
23
  )
31
24
 
32
- from vantage6.cli.common.utils import print_log_worker
33
- from vantage6.cli.context import AlgorithmStoreContext, ServerContext
34
- from vantage6.cli.globals import AlgoStoreGlobals, ServerGlobals
35
- from vantage6.cli.utils import (
36
- check_config_name_allowed,
37
- validate_input_cmd_args,
25
+ from vantage6.cli.common.utils import (
26
+ check_running,
27
+ select_context_and_namespace,
38
28
  )
29
+ from vantage6.cli.globals import ChartName
30
+ from vantage6.cli.utils import check_config_name_allowed, validate_input_cmd_args
39
31
 
40
32
 
41
- def check_for_start(ctx: AppContext, type_: InstanceType) -> DockerClient:
33
+ def prestart_checks(
34
+ ctx: AppContext,
35
+ instance_type: InstanceType,
36
+ name: str,
37
+ system_folders: bool,
38
+ context: str,
39
+ namespace: str,
40
+ ) -> None:
42
41
  """
43
- Check if all requirements are met to start the instance.
44
-
45
- Parameters
46
- ----------
47
- ctx : AppContext
48
- The context object
49
- type_ : InstanceType
50
- The type of instance to check for
51
-
52
- Returns
53
- -------
54
- DockerClient
55
- A Docker client instance
42
+ Run pre-start checks for an instance.
56
43
  """
57
- # will print an error if not
58
- check_docker_running()
59
-
60
- info("Finding Docker daemon.")
61
- docker_client = docker.from_env()
62
44
 
63
- # check if name is allowed for docker volume, else exit
64
45
  check_config_name_allowed(ctx.name)
65
46
 
66
- # check that this server is not already running
67
- running_servers = docker_client.containers.list(
68
- filters={"label": f"{APPNAME}-type={type_}"}
69
- )
70
- for server in running_servers:
71
- if server.name == f"{APPNAME}-{ctx.name}-{ctx.scope}-{type_}":
72
- error(f"Server {Fore.RED}{ctx.name}{Style.RESET_ALL} is already running")
73
- exit(1)
74
- return docker_client
75
-
76
-
77
- def get_image(
78
- image: str, ctx: AppContext, custom_image_key: str, default_image: str
79
- ) -> str:
80
- """
81
- Get the image name for the given instance type.
82
-
83
- Parameters
84
- ----------
85
- image : str | None
86
- The image name to use if specified
87
- ctx : AppContext
88
- The context object
89
- custom_image_key : str
90
- The key to look for in the config file
91
- default_image : str
92
- The default image name
47
+ if check_running(ctx.helm_release_name, instance_type, name, system_folders):
48
+ error(f"Instance '{name}' is already running.")
49
+ exit(1)
93
50
 
94
- Returns
95
- -------
96
- str
97
- The image name to use
98
- """
99
- # Determine image-name. First we check if the option --image has been used.
100
- # Then we check if the image has been specified in the config file, and
101
- # finally we use the default settings from the package.
102
- if image is None:
103
- custom_images: dict = ctx.config.get("images")
104
- if custom_images:
105
- image = custom_images.get(custom_image_key)
106
- if not image:
107
- image = f"{DEFAULT_DOCKER_REGISTRY}/{default_image}"
108
- return image
51
+ context, namespace = select_context_and_namespace(
52
+ context=context,
53
+ namespace=namespace,
54
+ )
109
55
 
110
56
 
111
57
  def pull_infra_image(
@@ -129,8 +75,8 @@ def pull_infra_image(
129
75
  try:
130
76
  pull_image(client, image, suppress_error=True)
131
77
  except docker.errors.APIError:
132
- if not is_default_infra_image(image, instance_type):
133
- if image_exists_locally(client, image):
78
+ if not _is_default_infra_image(image, instance_type):
79
+ if _image_exists_locally(client, image):
134
80
  warning("Failed to pull infrastructure image! Will use local image...")
135
81
  else:
136
82
  error("Failed to pull infrastructure image!")
@@ -141,7 +87,7 @@ def pull_infra_image(
141
87
  exit(1)
142
88
 
143
89
 
144
- def is_default_infra_image(image: str, instance_type: InstanceType) -> bool:
90
+ def _is_default_infra_image(image: str, instance_type: InstanceType) -> bool:
145
91
  """
146
92
  Check if an infrastructure image is the default image.
147
93
 
@@ -167,7 +113,7 @@ def is_default_infra_image(image: str, instance_type: InstanceType) -> bool:
167
113
  return image == f"{DEFAULT_DOCKER_REGISTRY}/{DEFAULT_NODE_IMAGE}"
168
114
 
169
115
 
170
- def image_exists_locally(client: DockerClient, image: str) -> bool:
116
+ def _image_exists_locally(client: DockerClient, image: str) -> bool:
171
117
  """
172
118
  Check if the image exists locally.
173
119
 
@@ -190,137 +136,9 @@ def image_exists_locally(client: DockerClient, image: str) -> bool:
190
136
  return True
191
137
 
192
138
 
193
- def mount_config_file(ctx: AppContext, config_file: str) -> list[docker.types.Mount]:
194
- """
195
- Mount the config file in the container.
196
-
197
- Parameters
198
- ----------
199
- ctx : AppContext
200
- The context object
201
- config_file : str
202
- The path to the config file
203
-
204
- Returns
205
- -------
206
- list[docker.types.Mount]
207
- The mounts to use
208
- """
209
- info("Creating mounts")
210
- return [docker.types.Mount(config_file, str(ctx.config_file), type="bind")]
211
-
212
-
213
- def mount_source(mount_src: str) -> docker.types.Mount:
214
- """
215
- Mount the vantage6 source code in the container.
216
-
217
- Parameters
218
- ----------
219
- mount_src : str
220
- The path to the source code
221
-
222
- Returns
223
- -------
224
- docker.types.Mount | None
225
- The mount to use
226
- """
227
- if mount_src:
228
- mount_src = os.path.abspath(mount_src)
229
- return docker.types.Mount("/vantage6", mount_src, type="bind")
230
-
231
-
232
- def mount_database(
233
- ctx: ServerContext | AlgorithmStoreContext, type_: InstanceType
234
- ) -> tuple[docker.types.Mount, dict]:
235
- """
236
- Mount database in the container if it is file-based (e.g. a SQLite DB).
237
-
238
- Parameters
239
- ----------
240
- ctx : AppContext
241
- The context object
242
- type_ : InstanceType
243
- The type of instance to mount the database for
244
-
245
- Returns
246
- -------
247
- docker.types.Mount | None
248
- The mount to use
249
- dict | None
250
- The environment variables to use
251
- """
252
- # FIXME: code duplication with cli_server_import()
253
- # try to mount database
254
- uri = ctx.config["uri"]
255
- url = make_url(uri)
256
- environment_vars = {}
257
- mount = None
258
-
259
- # If host is None, we're dealing with a file-based DB, like SQLite
260
- if url.host is None:
261
- db_path = url.database
262
-
263
- if not os.path.isabs(db_path):
264
- # We're dealing with a relative path here -> make it absolute
265
- db_path = ctx.data_dir / url.database
266
-
267
- basename = os.path.basename(db_path)
268
- dirname = os.path.dirname(db_path)
269
- os.makedirs(dirname, exist_ok=True)
270
-
271
- # we're mounting the entire folder that contains the database
272
- mount = docker.types.Mount("/mnt/database/", dirname, type="bind")
273
-
274
- if type_ == InstanceType.SERVER:
275
- environment_vars = {
276
- ServerGlobals.DB_URI_ENV_VAR.value: f"sqlite:////mnt/database/{basename}",
277
- ServerGlobals.CONFIG_NAME_ENV_VAR.value: ctx.config_file_name,
278
- }
279
- elif type_ == InstanceType.ALGORITHM_STORE:
280
- environment_vars = {
281
- AlgoStoreGlobals.DB_URI_ENV_VAR.value: f"sqlite:////mnt/database/{basename}",
282
- AlgoStoreGlobals.CONFIG_NAME_ENV_VAR.value: ctx.config_file_name,
283
- }
284
- else:
285
- warning(
286
- f"Database could not be transferred, make sure {url.host} "
287
- "is reachable from the Docker container"
288
- )
289
- info("Consider using the docker-compose method to start a server")
290
-
291
- return mount, environment_vars
292
-
293
-
294
- # TODO v5+ remove this function, it is replaced by the `attach_logs` function in
295
- # `vantage6.cli.common.utils`
296
- def attach_logs(container: Container, type_: InstanceType) -> None:
297
- """
298
- Attach container logs to the console if specified.
299
-
300
- Parameters
301
- ----------
302
- container : Container
303
- The container to attach the logs from
304
- type_ : InstanceType
305
- The type of instance to attach the logs for
306
- """
307
- logs = container.attach(stream=True, logs=True, stdout=True)
308
- Thread(target=print_log_worker, args=(logs,), daemon=True).start()
309
- while True:
310
- try:
311
- time.sleep(1)
312
- except KeyboardInterrupt:
313
- info("Closing log file. Keyboard Interrupt.")
314
- info(
315
- "Note that your server is still running! Shut it down "
316
- f"with {Fore.RED}v6 {type_} stop{Style.RESET_ALL}"
317
- )
318
- exit(0)
319
-
320
-
321
139
  def helm_install(
322
140
  release_name: str,
323
- chart_name: str,
141
+ chart_name: ChartName,
324
142
  values_file: str | PathLike | None = None,
325
143
  context: str | None = None,
326
144
  namespace: str | None = None,
@@ -361,6 +179,7 @@ def helm_install(
361
179
  chart_name,
362
180
  "--repo",
363
181
  DEFAULT_CHART_REPO,
182
+ # TODO v5+ remove this flag when we have a stable release
364
183
  "--devel", # ensure using latest version including pre-releases
365
184
  ]
366
185
 
@@ -380,14 +199,18 @@ def helm_install(
380
199
  check=True,
381
200
  )
382
201
  info(
383
- f"Successfully installed release '{release_name}' using chart '{chart_name}'."
202
+ f"Successfully installed release '{release_name}' using chart "
203
+ f"'{chart_name}'."
384
204
  )
385
- except subprocess.CalledProcessError as e:
386
- error(f"Failed to install release '{release_name}': {e.stderr}")
205
+ except subprocess.CalledProcessError:
206
+ error(f"Failed to install release '{release_name}'.")
207
+ exit(1)
387
208
  except FileNotFoundError:
388
209
  error(
389
- "Helm command not found. Please ensure Helm is installed and available in the PATH."
210
+ "Helm command not found. Please ensure Helm is installed and available in "
211
+ "the PATH."
390
212
  )
213
+ exit(1)
391
214
 
392
215
 
393
216
  def start_port_forward(
@@ -2,11 +2,89 @@ from __future__ import annotations
2
2
 
3
3
  import subprocess
4
4
 
5
+ from colorama import Fore, Style
5
6
 
6
7
  from vantage6.common import error, info, warning
8
+ from vantage6.common.globals import InstanceType
9
+
10
+ from vantage6.cli.common.utils import (
11
+ find_running_service_names,
12
+ select_context_and_namespace,
13
+ select_running_service,
14
+ )
15
+ from vantage6.cli.context import get_context
16
+ from vantage6.cli.globals import InfraComponentName
7
17
  from vantage6.cli.utils import validate_input_cmd_args
8
18
 
9
19
 
20
+ def execute_stop(
21
+ stop_function: callable,
22
+ instance_type: InstanceType,
23
+ infra_component: InfraComponentName,
24
+ stop_all: bool,
25
+ to_stop: str | None,
26
+ namespace: str,
27
+ context: str,
28
+ system_folders: bool,
29
+ ):
30
+ """
31
+ Execute the stop function for a given instance type and infra component.
32
+
33
+ Parameters
34
+ ----------
35
+ stop_function : callable
36
+ The function to stop the service.
37
+ instance_type : InstanceType
38
+ The instance type of the service.
39
+ infra_component : InfraComponentName
40
+ The infra component of the service.
41
+ stop_all : bool
42
+ Whether to stop all services.
43
+ to_stop : str | None
44
+ The name of the service to stop.
45
+ namespace : str
46
+ The namespace of the service.
47
+ context : str
48
+ The context of the service.
49
+ system_folders : bool
50
+ Whether to use system folders.
51
+ """
52
+ context, namespace = select_context_and_namespace(
53
+ context=context,
54
+ namespace=namespace,
55
+ )
56
+
57
+ running_services = find_running_service_names(
58
+ instance_type=instance_type,
59
+ only_system_folders=system_folders,
60
+ only_user_folders=not system_folders,
61
+ context=context,
62
+ namespace=namespace,
63
+ )
64
+
65
+ if not running_services:
66
+ error(f"No running {infra_component.value}s found.")
67
+ return
68
+
69
+ if stop_all:
70
+ for service in running_services:
71
+ stop_function(service, namespace, context)
72
+ else:
73
+ if not to_stop:
74
+ helm_name = select_running_service(running_services, instance_type)
75
+ else:
76
+ ctx = get_context(instance_type, to_stop, system_folders)
77
+ helm_name = ctx.helm_release_name
78
+
79
+ if helm_name in running_services:
80
+ stop_function(helm_name, namespace, context)
81
+ info(
82
+ f"Stopped the {Fore.GREEN}{helm_name}{Style.RESET_ALL} {infra_component.value}."
83
+ )
84
+ else:
85
+ error(f"{Fore.RED}{to_stop}{Style.RESET_ALL} is not running?!")
86
+
87
+
10
88
  def stop_port_forward(service_name: str) -> None:
11
89
  """
12
90
  Stop the port forwarding process for a given service name.