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.
- vantage6/cli/algorithm/generate_algorithm_json.py +9 -9
- vantage6/cli/algorithm/update.py +1 -1
- vantage6/cli/algostore/attach.py +1 -0
- vantage6/cli/algostore/files.py +3 -2
- vantage6/cli/algostore/list.py +0 -3
- vantage6/cli/algostore/new.py +83 -2
- vantage6/cli/algostore/remove.py +18 -34
- vantage6/cli/algostore/start.py +10 -7
- vantage6/cli/algostore/stop.py +12 -50
- vantage6/cli/auth/attach.py +60 -0
- vantage6/cli/auth/files.py +16 -0
- vantage6/cli/auth/list.py +13 -0
- vantage6/cli/auth/new.py +80 -0
- vantage6/cli/auth/remove.py +31 -0
- vantage6/cli/auth/start.py +80 -0
- vantage6/cli/auth/stop.py +64 -0
- vantage6/cli/cli.py +67 -37
- vantage6/cli/common/new.py +28 -3
- vantage6/cli/common/remove.py +54 -0
- vantage6/cli/common/start.py +31 -2
- vantage6/cli/common/stop.py +79 -1
- vantage6/cli/common/utils.py +47 -4
- vantage6/cli/configuration_manager.py +57 -13
- vantage6/cli/configuration_wizard.py +18 -397
- vantage6/cli/context/__init__.py +3 -0
- vantage6/cli/context/auth.py +107 -0
- vantage6/cli/context/base_server.py +0 -4
- vantage6/cli/context/node.py +10 -17
- vantage6/cli/dev/clean.py +28 -0
- vantage6/cli/dev/common.py +34 -0
- vantage6/cli/dev/rebuild.py +39 -0
- vantage6/cli/dev/start.py +36 -0
- vantage6/cli/dev/stop.py +23 -0
- vantage6/cli/globals.py +24 -1
- vantage6/cli/node/attach.py +1 -0
- vantage6/cli/node/files.py +12 -25
- vantage6/cli/node/list.py +5 -4
- vantage6/cli/node/new.py +348 -28
- vantage6/cli/node/remove.py +14 -90
- vantage6/cli/node/restart.py +30 -51
- vantage6/cli/node/start.py +81 -304
- vantage6/cli/node/stop.py +36 -96
- vantage6/cli/node/version.py +5 -4
- vantage6/cli/prometheus/monitoring_manager.py +5 -3
- vantage6/cli/rabbitmq/queue_manager.py +13 -11
- vantage6/cli/server/attach.py +1 -0
- vantage6/cli/server/common/__init__.py +1 -27
- vantage6/cli/server/import_.py +1 -1
- vantage6/cli/server/new.py +83 -2
- vantage6/cli/server/remove.py +12 -33
- vantage6/cli/server/start.py +8 -6
- vantage6/cli/server/stop.py +10 -39
- vantage6/cli/template/algo_store_config.j2 +1 -1
- vantage6/cli/template/auth_config.j2 +230 -0
- vantage6/cli/template/node_config.j2 +336 -33
- vantage6/cli/template/node_config_nonk8s.j2 +33 -0
- vantage6/cli/test/common/diagnostic_runner.py +5 -3
- vantage6/cli/use/namespace.py +2 -1
- vantage6/cli/utils.py +0 -2
- {vantage6-5.0.0a34.dist-info → vantage6-5.0.0a36.dist-info}/METADATA +3 -3
- vantage6-5.0.0a36.dist-info/RECORD +86 -0
- vantage6/cli/dev/create.py +0 -693
- vantage6/cli/dev/data/km_dataset.csv +0 -2401
- vantage6/cli/dev/remove.py +0 -112
- vantage6/cli/node/clean.py +0 -46
- vantage6/cli/server/shell.py +0 -54
- vantage6-5.0.0a34.dist-info/RECORD +0 -75
- {vantage6-5.0.0a34.dist-info → vantage6-5.0.0a36.dist-info}/WHEEL +0 -0
- {vantage6-5.0.0a34.dist-info → vantage6-5.0.0a36.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from vantage6.common import error
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def check_devspace_installed() -> None:
|
|
9
|
+
"""Check if devspace is installed. Exits if not."""
|
|
10
|
+
# Check if devspace command exists
|
|
11
|
+
if shutil.which("devspace") is None:
|
|
12
|
+
_message_devspace_not_installed()
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
# Try to run devspace --version to verify it's working
|
|
16
|
+
result = subprocess.run(
|
|
17
|
+
["devspace", "--version"], capture_output=True, text=True, timeout=10
|
|
18
|
+
)
|
|
19
|
+
if result.returncode != 0:
|
|
20
|
+
_message_devspace_not_installed()
|
|
21
|
+
except (
|
|
22
|
+
subprocess.TimeoutExpired,
|
|
23
|
+
subprocess.CalledProcessError,
|
|
24
|
+
FileNotFoundError,
|
|
25
|
+
):
|
|
26
|
+
_message_devspace_not_installed()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _message_devspace_not_installed() -> None:
|
|
30
|
+
error(
|
|
31
|
+
"❌ DevSpace command not found. Please ensure devspace is installed and in "
|
|
32
|
+
"your PATH."
|
|
33
|
+
)
|
|
34
|
+
sys.exit(1)
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
@click.option("--only-server", is_flag=True, help="Rebuild the server image.")
|
|
13
|
+
@click.option("--only-node", is_flag=True, help="Rebuild the node image.")
|
|
14
|
+
@click.option("--only-store", is_flag=True, help="Rebuild the store image.")
|
|
15
|
+
@click.option("--only-ui", is_flag=True, help="Rebuild the ui image.")
|
|
16
|
+
def cli_rebuild_dev_env(
|
|
17
|
+
only_server: bool, only_node: bool, only_store: bool, only_ui: bool
|
|
18
|
+
):
|
|
19
|
+
"""Rebuild Docker images for your development environment."""
|
|
20
|
+
check_devspace_installed()
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
info("🔄 Rebuilding development environment with devspace...")
|
|
24
|
+
cmd = ["devspace", "run", "rebuild"]
|
|
25
|
+
|
|
26
|
+
if only_server:
|
|
27
|
+
cmd.append("--server")
|
|
28
|
+
if only_node:
|
|
29
|
+
cmd.append("--node")
|
|
30
|
+
if only_store:
|
|
31
|
+
cmd.append("--store")
|
|
32
|
+
if only_ui:
|
|
33
|
+
cmd.append("--ui")
|
|
34
|
+
|
|
35
|
+
subprocess.run(cmd, check=True, capture_output=False)
|
|
36
|
+
info("✅ Development environment rebuilt successfully!")
|
|
37
|
+
except subprocess.CalledProcessError as e:
|
|
38
|
+
error(f"❌ Error rebuilding development environment: {e}")
|
|
39
|
+
sys.exit(e.returncode)
|
|
@@ -0,0 +1,36 @@
|
|
|
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_start_dev_env():
|
|
13
|
+
"""Start the development environment using devspace."""
|
|
14
|
+
check_devspace_installed()
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
info("🚀 Starting development environment with devspace...")
|
|
18
|
+
|
|
19
|
+
# Build the devspace command
|
|
20
|
+
cmd = ["devspace", "run", "start-dev"]
|
|
21
|
+
|
|
22
|
+
# Run the devspace command
|
|
23
|
+
result = subprocess.run(cmd, check=True, capture_output=False)
|
|
24
|
+
|
|
25
|
+
if result.returncode == 0:
|
|
26
|
+
info("✅ Development environment started successfully!")
|
|
27
|
+
else:
|
|
28
|
+
error("❌ Failed to start development environment.")
|
|
29
|
+
sys.exit(result.returncode)
|
|
30
|
+
|
|
31
|
+
except subprocess.CalledProcessError as e:
|
|
32
|
+
error(f"❌ Error running devspace: {e}")
|
|
33
|
+
sys.exit(e.returncode)
|
|
34
|
+
except Exception as e:
|
|
35
|
+
error(f"❌ Unexpected error: {e}")
|
|
36
|
+
sys.exit(1)
|
vantage6/cli/dev/stop.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
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_stop_dev_env():
|
|
13
|
+
"""Stop the development environment."""
|
|
14
|
+
check_devspace_installed()
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
info("🛑 Stopping development environment with devspace...")
|
|
18
|
+
cmd = ["devspace", "run", "stop-dev"]
|
|
19
|
+
subprocess.run(cmd, check=True, capture_output=False)
|
|
20
|
+
info("✅ Development environment stopped successfully!")
|
|
21
|
+
except subprocess.CalledProcessError as e:
|
|
22
|
+
error(f"❌ Error stopping development environment: {e}")
|
|
23
|
+
sys.exit(e.returncode)
|
vantage6/cli/globals.py
CHANGED
|
@@ -56,11 +56,12 @@ DEFAULT_PROMETHEUS_IMAGE = "prom/prometheus"
|
|
|
56
56
|
PROMETHEUS_CONFIG = "prometheus.yml"
|
|
57
57
|
PROMETHEUS_DIR = "prometheus"
|
|
58
58
|
|
|
59
|
-
# template
|
|
59
|
+
# template configuration files
|
|
60
60
|
TEMPLATE_FOLDER = PACKAGE_FOLDER / APPNAME / "cli" / "template"
|
|
61
61
|
SERVER_TEMPLATE_FILE = "server_config.j2"
|
|
62
62
|
NODE_TEMPLATE_FILE = "node_config.j2"
|
|
63
63
|
ALGO_STORE_TEMPLATE_FILE = "algo_store_config.j2"
|
|
64
|
+
AUTH_TEMPLATE_FILE = "auth_config.j2"
|
|
64
65
|
|
|
65
66
|
|
|
66
67
|
# datasets included in the nodes of the dev network
|
|
@@ -85,3 +86,25 @@ class ChartName(StrEnumBase):
|
|
|
85
86
|
ALGORITHM_STORE = "store"
|
|
86
87
|
NODE = "node"
|
|
87
88
|
AUTH = "auth"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class CLICommandName(StrEnumBase):
|
|
92
|
+
"""Enum containing CLI command names"""
|
|
93
|
+
|
|
94
|
+
SERVER = "server"
|
|
95
|
+
ALGORITHM_STORE = "algorithm-store"
|
|
96
|
+
NODE = "node"
|
|
97
|
+
ALGORITHM = "algorithm"
|
|
98
|
+
TEST = "test"
|
|
99
|
+
DEV = "dev"
|
|
100
|
+
USE = "use"
|
|
101
|
+
AUTH = "auth"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class InfraComponentName(StrEnumBase):
|
|
105
|
+
"""Enum containing infrastructure components"""
|
|
106
|
+
|
|
107
|
+
SERVER = "server"
|
|
108
|
+
ALGORITHM_STORE = "store"
|
|
109
|
+
NODE = "node"
|
|
110
|
+
AUTH = "auth"
|
vantage6/cli/node/attach.py
CHANGED
vantage6/cli/node/files.py
CHANGED
|
@@ -1,42 +1,29 @@
|
|
|
1
1
|
import click
|
|
2
2
|
|
|
3
3
|
from vantage6.common import info
|
|
4
|
+
from vantage6.common.globals import InstanceType
|
|
5
|
+
|
|
6
|
+
from vantage6.cli.common.decorator import click_insert_context
|
|
4
7
|
from vantage6.cli.context.node import NodeContext
|
|
5
|
-
from vantage6.cli.globals import DEFAULT_NODE_SYSTEM_FOLDERS as N_FOL
|
|
6
|
-
from vantage6.cli.node.common import select_node
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
@click.command()
|
|
10
|
-
@
|
|
11
|
-
|
|
12
|
-
"--system",
|
|
13
|
-
"system_folders",
|
|
14
|
-
flag_value=True,
|
|
15
|
-
help="Search for the configuration in the system folders",
|
|
16
|
-
)
|
|
17
|
-
@click.option(
|
|
18
|
-
"--user",
|
|
19
|
-
"system_folders",
|
|
20
|
-
flag_value=False,
|
|
21
|
-
default=N_FOL,
|
|
22
|
-
help="Search for the configuration in the user folders. This is " "the default",
|
|
23
|
-
)
|
|
24
|
-
def cli_node_files(name: str, system_folders: bool) -> None:
|
|
11
|
+
@click_insert_context(type_=InstanceType.NODE)
|
|
12
|
+
def cli_node_files(ctx: NodeContext) -> None:
|
|
25
13
|
"""
|
|
26
14
|
Prints the location of important node files.
|
|
27
15
|
|
|
28
16
|
If the specified configuration cannot be found, it exits. Otherwise
|
|
29
17
|
it returns the absolute path to the output.
|
|
30
18
|
"""
|
|
31
|
-
name = select_node(name, system_folders)
|
|
32
|
-
|
|
33
|
-
# create node context
|
|
34
|
-
ctx = NodeContext(name, system_folders=system_folders)
|
|
35
|
-
|
|
36
|
-
# return path of the configuration
|
|
37
19
|
info(f"Configuration file = {ctx.config_file}")
|
|
38
20
|
info(f"Log file = {ctx.log_file}")
|
|
39
21
|
info(f"data folders = {ctx.data_dir}")
|
|
40
22
|
info("Database labels and files")
|
|
41
|
-
for db in ctx.databases:
|
|
42
|
-
info(
|
|
23
|
+
for db in ctx.databases["fileBased"] or []:
|
|
24
|
+
info(
|
|
25
|
+
f" - {db['name']:15} = {db['volumePath']}/{db['originalName']} "
|
|
26
|
+
f"(type: {db['type']})"
|
|
27
|
+
)
|
|
28
|
+
for db in ctx.databases["serviceBased"] or []:
|
|
29
|
+
info(f" - {db['name']:15} = {db['uri']} (type: {db['type']})")
|
vantage6/cli/node/list.py
CHANGED
|
@@ -3,8 +3,9 @@ import docker
|
|
|
3
3
|
from colorama import Fore, Style
|
|
4
4
|
|
|
5
5
|
from vantage6.common import warning
|
|
6
|
-
from vantage6.common.globals import APPNAME
|
|
7
6
|
from vantage6.common.docker.addons import check_docker_running
|
|
7
|
+
from vantage6.common.globals import APPNAME
|
|
8
|
+
|
|
8
9
|
from vantage6.cli.context.node import NodeContext
|
|
9
10
|
from vantage6.cli.node.common import find_running_node_names
|
|
10
11
|
|
|
@@ -39,7 +40,7 @@ def cli_node_list() -> None:
|
|
|
39
40
|
if f"{APPNAME}-{config.name}-system" in running_node_names
|
|
40
41
|
else stopped
|
|
41
42
|
)
|
|
42
|
-
click.echo(f"{config.name:25}
|
|
43
|
+
click.echo(f"{config.name:25}{status:25}System ")
|
|
43
44
|
|
|
44
45
|
# user folders
|
|
45
46
|
configs, f2 = NodeContext.available_configurations(system_folders=False)
|
|
@@ -49,8 +50,8 @@ def cli_node_list() -> None:
|
|
|
49
50
|
if f"{APPNAME}-{config.name}-user" in running_node_names
|
|
50
51
|
else stopped
|
|
51
52
|
)
|
|
52
|
-
click.echo(f"{config.name:25}
|
|
53
|
+
click.echo(f"{config.name:25}{status:25}User ")
|
|
53
54
|
|
|
54
55
|
click.echo("-" * 53)
|
|
55
56
|
if len(f1) + len(f2):
|
|
56
|
-
warning(f"{Fore.RED}Failed imports: {len(f1)+len(f2)}{Style.RESET_ALL}")
|
|
57
|
+
warning(f"{Fore.RED}Failed imports: {len(f1) + len(f2)}{Style.RESET_ALL}")
|
vantage6/cli/node/new.py
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
1
4
|
import click
|
|
2
|
-
|
|
5
|
+
import questionary as q
|
|
3
6
|
|
|
4
|
-
from vantage6.common import
|
|
5
|
-
from vantage6.common.
|
|
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.
|
|
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
|
|
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(
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
#
|
|
50
|
-
|
|
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
|
-
|
|
53
|
-
except
|
|
54
|
-
error("
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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
|