vantage6 5.0.0a33__py3-none-any.whl → 5.0.0a34__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 +24 -46
- vantage6/cli/algostore/start.py +33 -67
- vantage6/cli/algostore/stop.py +75 -40
- vantage6/cli/common/new.py +60 -0
- vantage6/cli/common/start.py +17 -223
- vantage6/cli/common/stop.py +1 -1
- vantage6/cli/common/utils.py +212 -16
- vantage6/cli/configuration_manager.py +65 -0
- vantage6/cli/configuration_wizard.py +95 -76
- vantage6/cli/context/algorithm_store.py +7 -6
- vantage6/cli/context/base_server.py +22 -30
- vantage6/cli/context/node.py +4 -0
- vantage6/cli/context/server.py +16 -7
- vantage6/cli/globals.py +12 -11
- vantage6/cli/node/common/__init__.py +1 -1
- vantage6/cli/node/create_private_key.py +9 -6
- vantage6/cli/node/set_api_key.py +7 -4
- vantage6/cli/node/start.py +1 -1
- vantage6/cli/node/stop.py +7 -7
- vantage6/cli/server/import_.py +1 -2
- vantage6/cli/server/list.py +0 -3
- vantage6/cli/server/new.py +13 -51
- vantage6/cli/server/shell.py +1 -1
- vantage6/cli/server/start.py +17 -17
- vantage6/cli/server/stop.py +64 -15
- vantage6/cli/template/algo_store_config.j2 +195 -22
- vantage6/cli/template/server_config.j2 +255 -33
- {vantage6-5.0.0a33.dist-info → vantage6-5.0.0a34.dist-info}/METADATA +4 -4
- {vantage6-5.0.0a33.dist-info → vantage6-5.0.0a34.dist-info}/RECORD +31 -31
- vantage6/cli/template/server_import_config.j2 +0 -31
- {vantage6-5.0.0a33.dist-info → vantage6-5.0.0a34.dist-info}/WHEEL +0 -0
- {vantage6-5.0.0a33.dist-info → vantage6-5.0.0a34.dist-info}/entry_points.txt +0 -0
vantage6/cli/common/start.py
CHANGED
|
@@ -1,25 +1,17 @@
|
|
|
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
|
-
from vantage6.common.
|
|
20
|
-
from vantage6.common.docker.addons import check_docker_running, pull_image
|
|
13
|
+
from vantage6.common.docker.addons import pull_image
|
|
21
14
|
from vantage6.common.globals import (
|
|
22
|
-
APPNAME,
|
|
23
15
|
DEFAULT_ALGO_STORE_IMAGE,
|
|
24
16
|
DEFAULT_CHART_REPO,
|
|
25
17
|
DEFAULT_DOCKER_REGISTRY,
|
|
@@ -29,83 +21,8 @@ from vantage6.common.globals import (
|
|
|
29
21
|
InstanceType,
|
|
30
22
|
)
|
|
31
23
|
|
|
32
|
-
from vantage6.cli.
|
|
33
|
-
from vantage6.cli.
|
|
34
|
-
from vantage6.cli.globals import AlgoStoreGlobals, ServerGlobals
|
|
35
|
-
from vantage6.cli.utils import (
|
|
36
|
-
check_config_name_allowed,
|
|
37
|
-
validate_input_cmd_args,
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def check_for_start(ctx: AppContext, type_: InstanceType) -> DockerClient:
|
|
42
|
-
"""
|
|
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
|
|
56
|
-
"""
|
|
57
|
-
# will print an error if not
|
|
58
|
-
check_docker_running()
|
|
59
|
-
|
|
60
|
-
info("Finding Docker daemon.")
|
|
61
|
-
docker_client = docker.from_env()
|
|
62
|
-
|
|
63
|
-
# check if name is allowed for docker volume, else exit
|
|
64
|
-
check_config_name_allowed(ctx.name)
|
|
65
|
-
|
|
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
|
|
93
|
-
|
|
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
|
|
24
|
+
from vantage6.cli.globals import ChartName
|
|
25
|
+
from vantage6.cli.utils import validate_input_cmd_args
|
|
109
26
|
|
|
110
27
|
|
|
111
28
|
def pull_infra_image(
|
|
@@ -129,8 +46,8 @@ def pull_infra_image(
|
|
|
129
46
|
try:
|
|
130
47
|
pull_image(client, image, suppress_error=True)
|
|
131
48
|
except docker.errors.APIError:
|
|
132
|
-
if not
|
|
133
|
-
if
|
|
49
|
+
if not _is_default_infra_image(image, instance_type):
|
|
50
|
+
if _image_exists_locally(client, image):
|
|
134
51
|
warning("Failed to pull infrastructure image! Will use local image...")
|
|
135
52
|
else:
|
|
136
53
|
error("Failed to pull infrastructure image!")
|
|
@@ -141,7 +58,7 @@ def pull_infra_image(
|
|
|
141
58
|
exit(1)
|
|
142
59
|
|
|
143
60
|
|
|
144
|
-
def
|
|
61
|
+
def _is_default_infra_image(image: str, instance_type: InstanceType) -> bool:
|
|
145
62
|
"""
|
|
146
63
|
Check if an infrastructure image is the default image.
|
|
147
64
|
|
|
@@ -167,7 +84,7 @@ def is_default_infra_image(image: str, instance_type: InstanceType) -> bool:
|
|
|
167
84
|
return image == f"{DEFAULT_DOCKER_REGISTRY}/{DEFAULT_NODE_IMAGE}"
|
|
168
85
|
|
|
169
86
|
|
|
170
|
-
def
|
|
87
|
+
def _image_exists_locally(client: DockerClient, image: str) -> bool:
|
|
171
88
|
"""
|
|
172
89
|
Check if the image exists locally.
|
|
173
90
|
|
|
@@ -190,137 +107,9 @@ def image_exists_locally(client: DockerClient, image: str) -> bool:
|
|
|
190
107
|
return True
|
|
191
108
|
|
|
192
109
|
|
|
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
110
|
def helm_install(
|
|
322
111
|
release_name: str,
|
|
323
|
-
chart_name:
|
|
112
|
+
chart_name: ChartName,
|
|
324
113
|
values_file: str | PathLike | None = None,
|
|
325
114
|
context: str | None = None,
|
|
326
115
|
namespace: str | None = None,
|
|
@@ -361,6 +150,7 @@ def helm_install(
|
|
|
361
150
|
chart_name,
|
|
362
151
|
"--repo",
|
|
363
152
|
DEFAULT_CHART_REPO,
|
|
153
|
+
# TODO v5+ remove this flag when we have a stable release
|
|
364
154
|
"--devel", # ensure using latest version including pre-releases
|
|
365
155
|
]
|
|
366
156
|
|
|
@@ -380,14 +170,18 @@ def helm_install(
|
|
|
380
170
|
check=True,
|
|
381
171
|
)
|
|
382
172
|
info(
|
|
383
|
-
f"Successfully installed release '{release_name}' using chart
|
|
173
|
+
f"Successfully installed release '{release_name}' using chart "
|
|
174
|
+
f"'{chart_name}'."
|
|
384
175
|
)
|
|
385
|
-
except subprocess.CalledProcessError
|
|
386
|
-
error(f"Failed to install release '{release_name}'
|
|
176
|
+
except subprocess.CalledProcessError:
|
|
177
|
+
error(f"Failed to install release '{release_name}'.")
|
|
178
|
+
exit(1)
|
|
387
179
|
except FileNotFoundError:
|
|
388
180
|
error(
|
|
389
|
-
"Helm command not found. Please ensure Helm is installed and available in
|
|
181
|
+
"Helm command not found. Please ensure Helm is installed and available in "
|
|
182
|
+
"the PATH."
|
|
390
183
|
)
|
|
184
|
+
exit(1)
|
|
391
185
|
|
|
392
186
|
|
|
393
187
|
def start_port_forward(
|
vantage6/cli/common/stop.py
CHANGED
vantage6/cli/common/utils.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import json
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
2
4
|
from subprocess import Popen
|
|
3
5
|
from typing import Iterable
|
|
4
6
|
|
|
@@ -10,7 +12,174 @@ from colorama import Fore, Style
|
|
|
10
12
|
from vantage6.common import error, warning
|
|
11
13
|
from vantage6.common.globals import APPNAME, STRING_ENCODING, InstanceType
|
|
12
14
|
|
|
15
|
+
from vantage6.cli.config import CliConfig
|
|
13
16
|
from vantage6.cli.context import select_context_class
|
|
17
|
+
from vantage6.cli.utils import validate_input_cmd_args
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def select_context_and_namespace(
|
|
21
|
+
context: str | None = None,
|
|
22
|
+
namespace: str | None = None,
|
|
23
|
+
) -> tuple[str, str]:
|
|
24
|
+
"""
|
|
25
|
+
Select the context and namespace to use.
|
|
26
|
+
|
|
27
|
+
This uses the CLI config to compare the provided context and namespace with the
|
|
28
|
+
last used context and namespace. If the provided context and namespace are not
|
|
29
|
+
the same as the last used context and namespace, the CLI config is updated.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
context : str, optional
|
|
34
|
+
The Kubernetes context to use.
|
|
35
|
+
namespace : str, optional
|
|
36
|
+
The Kubernetes namespace to use.
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
tuple[str, str]
|
|
41
|
+
The context and namespace to use
|
|
42
|
+
"""
|
|
43
|
+
cli_config = CliConfig()
|
|
44
|
+
|
|
45
|
+
return cli_config.compare_changes_config(
|
|
46
|
+
context=context,
|
|
47
|
+
namespace=namespace,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def create_directory_if_not_exists(directory: Path) -> None:
|
|
52
|
+
"""
|
|
53
|
+
Create a directory.
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
57
|
+
except Exception as e:
|
|
58
|
+
error(f"Failed to create directory {directory}: {e}")
|
|
59
|
+
exit(1)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def find_running_service_names(
|
|
63
|
+
instance_type: InstanceType,
|
|
64
|
+
only_system_folders: bool = False,
|
|
65
|
+
only_user_folders: bool = False,
|
|
66
|
+
context: str | None = None,
|
|
67
|
+
namespace: str | None = None,
|
|
68
|
+
) -> list[str]:
|
|
69
|
+
"""
|
|
70
|
+
List running Vantage6 servers.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
instance_type : InstanceType
|
|
75
|
+
The type of instance to find running services for
|
|
76
|
+
only_system_folders : bool, optional
|
|
77
|
+
Whether to look for system-based services or not. By default False.
|
|
78
|
+
only_user_folders : bool, optional
|
|
79
|
+
Whether to look for user-based services or not. By default False.
|
|
80
|
+
context : str, optional
|
|
81
|
+
The Kubernetes context to use.
|
|
82
|
+
namespace : str, optional
|
|
83
|
+
The Kubernetes namespace to use.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
list[str]
|
|
88
|
+
List of release names that are running
|
|
89
|
+
"""
|
|
90
|
+
# Input validation
|
|
91
|
+
validate_input_cmd_args(context, "context name", allow_none=True)
|
|
92
|
+
validate_input_cmd_args(namespace, "namespace name", allow_none=True)
|
|
93
|
+
validate_input_cmd_args(instance_type, "instance type", allow_none=False)
|
|
94
|
+
if only_system_folders and only_user_folders:
|
|
95
|
+
error("Cannot use both only_system_folders and only_user_folders")
|
|
96
|
+
exit(1)
|
|
97
|
+
|
|
98
|
+
# Create the command
|
|
99
|
+
command = [
|
|
100
|
+
"helm",
|
|
101
|
+
"list",
|
|
102
|
+
"--output",
|
|
103
|
+
"json", # Get structured output
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
if context:
|
|
107
|
+
command.extend(["--kube-context", context])
|
|
108
|
+
|
|
109
|
+
if namespace:
|
|
110
|
+
command.extend(["--namespace", namespace])
|
|
111
|
+
else:
|
|
112
|
+
command.extend(["--all-namespaces"])
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
result = subprocess.run(
|
|
116
|
+
command,
|
|
117
|
+
capture_output=True,
|
|
118
|
+
text=True,
|
|
119
|
+
check=True,
|
|
120
|
+
)
|
|
121
|
+
except subprocess.CalledProcessError as e:
|
|
122
|
+
error(f"Failed to list Helm releases: {e}")
|
|
123
|
+
return []
|
|
124
|
+
except FileNotFoundError:
|
|
125
|
+
error(
|
|
126
|
+
"Helm command not found. Please ensure Helm is installed and available in "
|
|
127
|
+
"the PATH."
|
|
128
|
+
)
|
|
129
|
+
return []
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
releases = json.loads(result.stdout)
|
|
133
|
+
except json.JSONDecodeError:
|
|
134
|
+
error("Failed to parse Helm output as JSON")
|
|
135
|
+
return []
|
|
136
|
+
|
|
137
|
+
# filter services for the vantage6 services that are sought. These have
|
|
138
|
+
# the following pattern:
|
|
139
|
+
# f"{APPNAME}-{name}-{scope}-{instance_type.value}"
|
|
140
|
+
|
|
141
|
+
# filter for the instance type
|
|
142
|
+
svc_starts_with = f"{APPNAME}-"
|
|
143
|
+
if only_system_folders:
|
|
144
|
+
svc_ends_with = f"system-{instance_type.value}"
|
|
145
|
+
elif only_user_folders:
|
|
146
|
+
svc_ends_with = f"user-{instance_type.value}"
|
|
147
|
+
else:
|
|
148
|
+
svc_ends_with = f"-{instance_type.value}"
|
|
149
|
+
|
|
150
|
+
matching_services = []
|
|
151
|
+
for release in releases:
|
|
152
|
+
release_name = release.get("name", "")
|
|
153
|
+
|
|
154
|
+
# Check if this is a Vantage6 server release
|
|
155
|
+
is_matching_service = (
|
|
156
|
+
release_name.startswith(svc_starts_with)
|
|
157
|
+
and release_name.endswith(svc_ends_with)
|
|
158
|
+
and instance_type.value in release_name
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
if is_matching_service:
|
|
162
|
+
matching_services.append(release_name)
|
|
163
|
+
|
|
164
|
+
return matching_services
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def select_running_service(
|
|
168
|
+
running_services: list[str],
|
|
169
|
+
instance_type: InstanceType,
|
|
170
|
+
) -> str:
|
|
171
|
+
"""
|
|
172
|
+
Select a running service from the list of running services.
|
|
173
|
+
"""
|
|
174
|
+
try:
|
|
175
|
+
name = q.select(
|
|
176
|
+
f"Select the {instance_type.value} you wish to inspect:",
|
|
177
|
+
choices=running_services,
|
|
178
|
+
).unsafe_ask()
|
|
179
|
+
except KeyboardInterrupt:
|
|
180
|
+
error("Aborted by user!")
|
|
181
|
+
exit(1)
|
|
182
|
+
return name
|
|
14
183
|
|
|
15
184
|
|
|
16
185
|
def get_server_name(
|
|
@@ -20,30 +189,30 @@ def get_server_name(
|
|
|
20
189
|
instance_type: InstanceType,
|
|
21
190
|
) -> str:
|
|
22
191
|
"""
|
|
23
|
-
Get the
|
|
192
|
+
Get the full name of a running server.
|
|
24
193
|
|
|
25
194
|
Parameters
|
|
26
195
|
----------
|
|
27
196
|
name : str
|
|
28
|
-
Name of the server to get the
|
|
197
|
+
Name of the server to get the full name from
|
|
29
198
|
system_folders : bool
|
|
30
199
|
Whether to use system folders or not
|
|
31
200
|
running_server_names : list[str]
|
|
32
201
|
The names of the running servers
|
|
33
202
|
instance_type : InstanceType
|
|
34
|
-
The type of instance to get the
|
|
203
|
+
The type of instance to get the full name from
|
|
35
204
|
"""
|
|
36
205
|
|
|
37
206
|
if not name:
|
|
38
207
|
if not running_server_names:
|
|
39
208
|
error(
|
|
40
|
-
f"No {instance_type}s are running! You can only check the version
|
|
41
|
-
f"{instance_type}s that are running"
|
|
209
|
+
f"No {instance_type.value}s are running! You can only check the version"
|
|
210
|
+
f" for {instance_type.value}s that are running"
|
|
42
211
|
)
|
|
43
212
|
exit(1)
|
|
44
213
|
try:
|
|
45
214
|
name = q.select(
|
|
46
|
-
f"Select the {instance_type} you wish to inspect:",
|
|
215
|
+
f"Select the {instance_type.value} you wish to inspect:",
|
|
47
216
|
choices=running_server_names,
|
|
48
217
|
).unsafe_ask()
|
|
49
218
|
except KeyboardInterrupt:
|
|
@@ -73,7 +242,7 @@ def get_running_servers(
|
|
|
73
242
|
The names of the running servers
|
|
74
243
|
"""
|
|
75
244
|
running_servers = client.containers.list(
|
|
76
|
-
filters={"label": f"{APPNAME}-type={instance_type}"}
|
|
245
|
+
filters={"label": f"{APPNAME}-type={instance_type.value}"}
|
|
77
246
|
)
|
|
78
247
|
return [server.name for server in running_servers]
|
|
79
248
|
|
|
@@ -87,10 +256,9 @@ def get_server_configuration_list(instance_type: InstanceType) -> None:
|
|
|
87
256
|
instance_type : InstanceType
|
|
88
257
|
The type of instance to get the configurations for
|
|
89
258
|
"""
|
|
90
|
-
client = docker.from_env()
|
|
91
259
|
ctx_class = select_context_class(instance_type)
|
|
92
260
|
|
|
93
|
-
running_server_names =
|
|
261
|
+
running_server_names = find_running_service_names(instance_type)
|
|
94
262
|
header = "\nName" + (21 * " ") + "Status" + (10 * " ") + "System/User"
|
|
95
263
|
|
|
96
264
|
click.echo(header)
|
|
@@ -100,28 +268,37 @@ def get_server_configuration_list(instance_type: InstanceType) -> None:
|
|
|
100
268
|
stopped = Fore.RED + "Not running" + Style.RESET_ALL
|
|
101
269
|
|
|
102
270
|
# system folders
|
|
103
|
-
configs,
|
|
271
|
+
configs, failed_imports_system = ctx_class.available_configurations(
|
|
272
|
+
system_folders=True
|
|
273
|
+
)
|
|
104
274
|
for config in configs:
|
|
105
275
|
status = (
|
|
106
276
|
running
|
|
107
|
-
if f"{APPNAME}-{config.name}-system-{instance_type}"
|
|
277
|
+
if f"{APPNAME}-{config.name}-system-{instance_type.value}"
|
|
278
|
+
in running_server_names
|
|
108
279
|
else stopped
|
|
109
280
|
)
|
|
110
281
|
click.echo(f"{config.name:25}{status:25} System ")
|
|
111
282
|
|
|
112
283
|
# user folders
|
|
113
|
-
configs,
|
|
284
|
+
configs, failed_imports_user = ctx_class.available_configurations(
|
|
285
|
+
system_folders=False
|
|
286
|
+
)
|
|
114
287
|
for config in configs:
|
|
115
288
|
status = (
|
|
116
289
|
running
|
|
117
|
-
if f"{APPNAME}-{config.name}-user-{instance_type}"
|
|
290
|
+
if f"{APPNAME}-{config.name}-user-{instance_type.value}"
|
|
291
|
+
in running_server_names
|
|
118
292
|
else stopped
|
|
119
293
|
)
|
|
120
294
|
click.echo(f"{config.name:25}{status:25} User ")
|
|
121
295
|
|
|
122
296
|
click.echo("-" * 85)
|
|
123
|
-
if len(
|
|
124
|
-
warning(
|
|
297
|
+
if len(failed_imports_system) + len(failed_imports_user):
|
|
298
|
+
warning(
|
|
299
|
+
f"{Fore.RED}Failed imports: "
|
|
300
|
+
f"{len(failed_imports_system) + len(failed_imports_user)}{Style.RESET_ALL}"
|
|
301
|
+
)
|
|
125
302
|
|
|
126
303
|
|
|
127
304
|
def print_log_worker(logs_stream: Iterable[bytes]) -> None:
|
|
@@ -175,3 +352,22 @@ def attach_logs(*labels: list[str]) -> None:
|
|
|
175
352
|
command = ["kubectl", "logs", "--follow", "--selector", ",".join(labels)]
|
|
176
353
|
process = Popen(command, stdout=None, stderr=None)
|
|
177
354
|
process.wait()
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def get_main_cli_command_name(instance_type: InstanceType) -> str:
|
|
358
|
+
"""
|
|
359
|
+
Get the main CLI command name for a given instance type.
|
|
360
|
+
|
|
361
|
+
Parameters
|
|
362
|
+
----------
|
|
363
|
+
instance_type : InstanceType
|
|
364
|
+
The type of instance to get the main CLI command name for
|
|
365
|
+
"""
|
|
366
|
+
if instance_type == InstanceType.SERVER:
|
|
367
|
+
return "server"
|
|
368
|
+
elif instance_type == InstanceType.ALGORITHM_STORE:
|
|
369
|
+
return "algorithm-store"
|
|
370
|
+
elif instance_type == InstanceType.NODE:
|
|
371
|
+
return "node"
|
|
372
|
+
else:
|
|
373
|
+
raise ValueError(f"Invalid instance type: {instance_type}")
|
|
@@ -4,6 +4,8 @@ from schema import And, Optional, Or, Use
|
|
|
4
4
|
|
|
5
5
|
from vantage6.common.configuration_manager import Configuration, ConfigurationManager
|
|
6
6
|
|
|
7
|
+
from vantage6.cli.globals import ALGO_STORE_TEMPLATE_FILE, SERVER_TEMPLATE_FILE
|
|
8
|
+
|
|
7
9
|
LOGGING_VALIDATORS = {
|
|
8
10
|
"level": And(
|
|
9
11
|
Use(str), lambda lvl: lvl in ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL")
|
|
@@ -26,6 +28,15 @@ class ServerConfiguration(Configuration):
|
|
|
26
28
|
VALIDATORS = {}
|
|
27
29
|
|
|
28
30
|
|
|
31
|
+
class AlgorithmStoreConfiguration(Configuration):
|
|
32
|
+
"""
|
|
33
|
+
Stores the algorithm store's configuration and defines a set of algorithm store-specific
|
|
34
|
+
validators.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
VALIDATORS = {}
|
|
38
|
+
|
|
39
|
+
|
|
29
40
|
class NodeConfiguration(Configuration):
|
|
30
41
|
"""
|
|
31
42
|
Stores the node's configuration and defines a set of node-specific
|
|
@@ -114,6 +125,60 @@ class ServerConfigurationManager(ConfigurationManager):
|
|
|
114
125
|
"""
|
|
115
126
|
return super().from_file(path, conf_class=ServerConfiguration)
|
|
116
127
|
|
|
128
|
+
def get_config_template(self) -> str:
|
|
129
|
+
"""
|
|
130
|
+
Get the configuration template for the server.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
str
|
|
135
|
+
The configuration template for the server.
|
|
136
|
+
"""
|
|
137
|
+
return super()._get_config_template(SERVER_TEMPLATE_FILE)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class AlgorithmStoreConfigurationManager(ConfigurationManager):
|
|
141
|
+
"""
|
|
142
|
+
Maintains the algorithm store's configuration.
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
name : str
|
|
147
|
+
Name of the configuration file.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
def __init__(self, name, *args, **kwargs) -> None:
|
|
151
|
+
super().__init__(conf_class=AlgorithmStoreConfiguration, name=name)
|
|
152
|
+
|
|
153
|
+
@classmethod
|
|
154
|
+
def from_file(cls, path: str) -> Self:
|
|
155
|
+
"""
|
|
156
|
+
Create a new instance of the AlgorithmStoreConfigurationManager from a
|
|
157
|
+
configuration file.
|
|
158
|
+
|
|
159
|
+
Parameters
|
|
160
|
+
----------
|
|
161
|
+
path : str
|
|
162
|
+
Path to the configuration file.
|
|
163
|
+
|
|
164
|
+
Returns
|
|
165
|
+
-------
|
|
166
|
+
AlgorithmStoreConfigurationManager
|
|
167
|
+
A new instance of the AlgorithmStoreConfigurationManager.
|
|
168
|
+
"""
|
|
169
|
+
return super().from_file(path, conf_class=AlgorithmStoreConfiguration)
|
|
170
|
+
|
|
171
|
+
def get_config_template(self) -> str:
|
|
172
|
+
"""
|
|
173
|
+
Get the configuration template for the algorithm store.
|
|
174
|
+
|
|
175
|
+
Returns
|
|
176
|
+
-------
|
|
177
|
+
str
|
|
178
|
+
The configuration template for the algorithm store.
|
|
179
|
+
"""
|
|
180
|
+
return super()._get_config_template(ALGO_STORE_TEMPLATE_FILE)
|
|
181
|
+
|
|
117
182
|
|
|
118
183
|
class TestingConfigurationManager(ConfigurationManager):
|
|
119
184
|
def __init__(self, name, *args, **kwargs):
|