vantage6 5.0.0a29__py3-none-any.whl → 5.0.0a33__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/__init__.py +5 -1
- vantage6/cli/algorithm/generate_algorithm_json.py +3 -11
- vantage6/cli/context/algorithm_store.py +1 -1
- vantage6/cli/context/node.py +7 -2
- vantage6/cli/context/server.py +1 -1
- vantage6-5.0.0a33.dist-info/METADATA +54 -0
- {vantage6-5.0.0a29.dist-info → vantage6-5.0.0a33.dist-info}/RECORD +9 -18
- {vantage6-5.0.0a29.dist-info → vantage6-5.0.0a33.dist-info}/WHEEL +1 -2
- vantage6-5.0.0a33.dist-info/entry_points.txt +2 -0
- tests_cli/__init__.py +0 -0
- tests_cli/test_client_script.py +0 -23
- tests_cli/test_example.py +0 -7
- tests_cli/test_node_cli.py +0 -452
- tests_cli/test_server_cli.py +0 -180
- tests_cli/test_wizard.py +0 -141
- vantage6/cli/__build__ +0 -1
- vantage6/cli/_version.py +0 -23
- vantage6-5.0.0a29.dist-info/METADATA +0 -238
- vantage6-5.0.0a29.dist-info/entry_points.txt +0 -5
- vantage6-5.0.0a29.dist-info/top_level.txt +0 -2
vantage6/cli/__init__.py
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
"""Command line interface for the vantage6 infrastructure."""
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import importlib.metadata
|
|
4
|
+
|
|
5
|
+
# note that here we cannot use __package__ because __package__ resolves to vantage6.cli
|
|
6
|
+
# whereas the PyPi package is called vantage6
|
|
7
|
+
__version__ = importlib.metadata.version("vantage6")
|
|
@@ -19,8 +19,6 @@ from vantage6.common.algorithm_function import (
|
|
|
19
19
|
)
|
|
20
20
|
from vantage6.common.enum import AlgorithmArgumentType, AlgorithmStepType, StrEnumBase
|
|
21
21
|
|
|
22
|
-
from vantage6.algorithm.tools import DecoratorStepType
|
|
23
|
-
|
|
24
22
|
from vantage6.algorithm.client import AlgorithmClient
|
|
25
23
|
from vantage6.algorithm.preprocessing.algorithm_json_data import (
|
|
26
24
|
PREPROCESSING_FUNCTIONS_JSON_DATA,
|
|
@@ -120,7 +118,7 @@ class Function:
|
|
|
120
118
|
# infrastructure-defined function
|
|
121
119
|
if (
|
|
122
120
|
not self.func.__module__.startswith("vantage6.algorithm.")
|
|
123
|
-
or
|
|
121
|
+
or self.json["name"] not in PREPROCESSING_FUNCTIONS_JSON_DATA
|
|
124
122
|
):
|
|
125
123
|
return
|
|
126
124
|
|
|
@@ -327,14 +325,8 @@ class Function:
|
|
|
327
325
|
def _get_step_type(self) -> AlgorithmStepType | None:
|
|
328
326
|
"""Get the step type of the function"""
|
|
329
327
|
decorator_type = get_vantage6_decorator_type(self.func)
|
|
330
|
-
if decorator_type
|
|
331
|
-
return
|
|
332
|
-
elif decorator_type == DecoratorStepType.CENTRAL:
|
|
333
|
-
return AlgorithmStepType.CENTRAL_COMPUTE
|
|
334
|
-
elif decorator_type == DecoratorStepType.PREPROCESSING:
|
|
335
|
-
return AlgorithmStepType.PREPROCESSING
|
|
336
|
-
elif decorator_type == DecoratorStepType.DATA_EXTRACTION:
|
|
337
|
-
return AlgorithmStepType.DATA_EXTRACTION
|
|
328
|
+
if decorator_type in AlgorithmStepType.list():
|
|
329
|
+
return decorator_type
|
|
338
330
|
else:
|
|
339
331
|
warning(
|
|
340
332
|
f"Unsupported decorator type: {decorator_type} for function {self.name}"
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from vantage6.common.globals import APPNAME, InstanceType
|
|
4
4
|
|
|
5
|
-
from vantage6.cli
|
|
5
|
+
from vantage6.cli import __version__
|
|
6
6
|
from vantage6.cli.configuration_manager import ServerConfigurationManager
|
|
7
7
|
from vantage6.cli.context.base_server import BaseServerContext
|
|
8
8
|
from vantage6.cli.globals import (
|
vantage6/cli/context/node.py
CHANGED
|
@@ -7,7 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
from vantage6.common.context import AppContext
|
|
8
8
|
from vantage6.common.globals import APPNAME, STRING_ENCODING, InstanceType
|
|
9
9
|
|
|
10
|
-
from vantage6.cli
|
|
10
|
+
from vantage6.cli import __version__
|
|
11
11
|
from vantage6.cli.configuration_manager import NodeConfigurationManager
|
|
12
12
|
from vantage6.cli.globals import DEFAULT_NODE_SYSTEM_FOLDERS as N_FOL
|
|
13
13
|
|
|
@@ -40,7 +40,12 @@ class NodeContext(AppContext):
|
|
|
40
40
|
logger_prefix: str = "",
|
|
41
41
|
):
|
|
42
42
|
super().__init__(
|
|
43
|
-
InstanceType.NODE,
|
|
43
|
+
InstanceType.NODE,
|
|
44
|
+
instance_name,
|
|
45
|
+
system_folders=system_folders,
|
|
46
|
+
config_file=config_file,
|
|
47
|
+
print_log_header=print_log_header,
|
|
48
|
+
logger_prefix=logger_prefix,
|
|
44
49
|
)
|
|
45
50
|
if print_log_header:
|
|
46
51
|
self.log.info("vantage6 version '%s'", __version__)
|
vantage6/cli/context/server.py
CHANGED
|
@@ -4,7 +4,7 @@ from pathlib import Path
|
|
|
4
4
|
|
|
5
5
|
from vantage6.common.globals import APPNAME, InstanceType
|
|
6
6
|
|
|
7
|
-
from vantage6.cli
|
|
7
|
+
from vantage6.cli import __version__
|
|
8
8
|
from vantage6.cli.configuration_manager import ServerConfigurationManager
|
|
9
9
|
from vantage6.cli.context.base_server import BaseServerContext
|
|
10
10
|
from vantage6.cli.globals import (
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vantage6
|
|
3
|
+
Version: 5.0.0a33
|
|
4
|
+
Summary: vantage6 command line interface
|
|
5
|
+
Author: Vantage6 Team
|
|
6
|
+
Maintainer-email: Frank Martin <f.martin@iknl.nl>, Bart van Beusekom <b.vanbeusekom@iknl.nl>
|
|
7
|
+
License: MIT
|
|
8
|
+
Requires-Python: >=3.13
|
|
9
|
+
Requires-Dist: click==8.1.3
|
|
10
|
+
Requires-Dist: colorama==0.4.6
|
|
11
|
+
Requires-Dist: copier==9.2.0
|
|
12
|
+
Requires-Dist: docker==7.1.0
|
|
13
|
+
Requires-Dist: ipython==8.10.0
|
|
14
|
+
Requires-Dist: jinja2==3.1.6
|
|
15
|
+
Requires-Dist: kubernetes==28.1.0
|
|
16
|
+
Requires-Dist: pandas>=2.2.3
|
|
17
|
+
Requires-Dist: questionary==1.10.0
|
|
18
|
+
Requires-Dist: rich==13.5.2
|
|
19
|
+
Requires-Dist: schema==0.7.5
|
|
20
|
+
Requires-Dist: sqlalchemy==2.0.37
|
|
21
|
+
Requires-Dist: vantage6-client==5.0.0a33
|
|
22
|
+
Requires-Dist: vantage6-common==5.0.0a33
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: black; extra == 'dev'
|
|
25
|
+
Requires-Dist: coverage==7.10.2; extra == 'dev'
|
|
26
|
+
Requires-Dist: pre-commit; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
<h1 align="center">
|
|
30
|
+
<br>
|
|
31
|
+
<a href="https://vantage6.ai"><img src="https://github.com/IKNL/guidelines/blob/master/resources/logos/vantage6.png?raw=true" alt="vantage6" width="350"></a>
|
|
32
|
+
</h1>
|
|
33
|
+
|
|
34
|
+
<h3 align=center> A Privacy Enhancing Technology (PET) Operations platform</h3>
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
# Vantage6 CLI
|
|
39
|
+
|
|
40
|
+
This package provides the command-line interface (CLI) for managing vantage6
|
|
41
|
+
infrastructure instances, such as servers and nodes.
|
|
42
|
+
|
|
43
|
+
## Documentation
|
|
44
|
+
|
|
45
|
+
For complete documentation, installation instructions, and usage examples, please see
|
|
46
|
+
the **[main Vantage6 README](https://github.com/vantage6/vantage6/blob/main/README.md)**
|
|
47
|
+
.
|
|
48
|
+
|
|
49
|
+
## About Vantage6
|
|
50
|
+
|
|
51
|
+
Vantage6 is a Privacy Enhancing Technology (PET) Operations platform that enables
|
|
52
|
+
federated learning and multi-party computation. For more information, visit
|
|
53
|
+
[vantage6.ai](https://vantage6.ai) or join our
|
|
54
|
+
[Discord community](https://discord.gg/yAyFf6Y).
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
tests_cli/test_client_script.py,sha256=_Wf8nKEfq6HEnfUQtwZvyICEmEa1TngyI_orPRQ7ZNQ,683
|
|
3
|
-
tests_cli/test_example.py,sha256=0fw_v-lgZEacshWSDwLNyLMA1_xc48bKUGM3ll-n1L0,146
|
|
4
|
-
tests_cli/test_node_cli.py,sha256=jM-nZkcvdEwwHVgQi8C7cYAbTJOzVmFl60MFXYVH9ig,16798
|
|
5
|
-
tests_cli/test_server_cli.py,sha256=i0XBybUPn0_ACOHBi8d6vFETut7szz3Zt8ttmZhWVPc,6545
|
|
6
|
-
tests_cli/test_wizard.py,sha256=ds2gJeBasQ06-nzqsPSSxrj-COe4pCLPgqrlBVkCXkw,5021
|
|
7
|
-
vantage6/cli/__build__,sha256=NRNaqmzCOJG0DLPzeMU6F6ESchDOYOElzPA-_P2uxFg,2
|
|
8
|
-
vantage6/cli/__init__.py,sha256=ZXbeQ_-g2-M4XYteWZkoO5lMFYhqjm5doQgGy1fq8i0,125
|
|
9
|
-
vantage6/cli/_version.py,sha256=iDijqhgy5jzZ0LAyzW1LlXeeuMcHWMyg9D8xbXtV7Ck,696
|
|
1
|
+
vantage6/cli/__init__.py,sha256=8KIgMPbZTNdkYlUrB8irpsUA7KdkFdLbNbMKixOP2dE,277
|
|
10
2
|
vantage6/cli/cli.py,sha256=EeMfvBABKYauujZDR09IjpBgtbBg1sS1iJPuzre-AqM,6474
|
|
11
3
|
vantage6/cli/config.py,sha256=Jqe3VcvoxPrFlvw7cuKt4-5oyL0YTZXvpNsqPybSAYk,7973
|
|
12
4
|
vantage6/cli/configuration_manager.py,sha256=FV0RmiKmaGI2le8coUA4R5NTFUgPeph5XimxqgDhIsg,3332
|
|
@@ -14,7 +6,7 @@ vantage6/cli/configuration_wizard.py,sha256=6UB4YwBlCjRT88bHgp6VeDFgQklAM2g2WXS1
|
|
|
14
6
|
vantage6/cli/globals.py,sha256=CxpxWXBLvAJR7st4-iRh95xbBZrn3qxKdbW3_7wmKY8,2245
|
|
15
7
|
vantage6/cli/utils.py,sha256=mhCtsxCf8nOCaW3layW8aJGI0XAjGs0IuheCNj3o-IA,4397
|
|
16
8
|
vantage6/cli/algorithm/create.py,sha256=kRT1BlBcb0fDaB2Q988WxtA6EyAZmOW5QoU2uhbwBIo,2075
|
|
17
|
-
vantage6/cli/algorithm/generate_algorithm_json.py,sha256=
|
|
9
|
+
vantage6/cli/algorithm/generate_algorithm_json.py,sha256=huaqoadhz-2-Sy6SON9zFe_cAatvE7dtTQazvmNOgMA,19558
|
|
18
10
|
vantage6/cli/algorithm/update.py,sha256=WwAfTnq0kTOgePUsBzGoo1AJQqGMn82E9Bjk1wf61CQ,1338
|
|
19
11
|
vantage6/cli/algostore/attach.py,sha256=0WzLnKigJAelPpL5EaWcnRuUyQzMSofrrZfEzwDIvSw,311
|
|
20
12
|
vantage6/cli/algostore/files.py,sha256=r89VRixK_K-c44qseq58Aa2Dqf1wEf8yompQRN5AVu4,580
|
|
@@ -28,10 +20,10 @@ vantage6/cli/common/start.py,sha256=6L_6NraS1U9Gj_UiQYkg4UE4bwjQkCQdTigb1L6fCm4,
|
|
|
28
20
|
vantage6/cli/common/stop.py,sha256=CxW5OFZT7eacpln9J05ble2Q3xHCqyxYqJ00rPUbrVo,2645
|
|
29
21
|
vantage6/cli/common/utils.py,sha256=z9NX_2hft2bkIvilnrtLV92ui62pIEhj-f3gCJcjcQ4,4970
|
|
30
22
|
vantage6/cli/context/__init__.py,sha256=88LSA3h_v-t8PzjCrzl0vw1jkOdNUNr3nPo-L2e9onc,2684
|
|
31
|
-
vantage6/cli/context/algorithm_store.py,sha256=
|
|
23
|
+
vantage6/cli/context/algorithm_store.py,sha256=s6T5coyGHyXHupE1EFvcXCL8A69a2gUe5-6w6jNNKko,3967
|
|
32
24
|
vantage6/cli/context/base_server.py,sha256=paKSzNrKWD-J6eakHAtGELk2cD05A8NqoCAuQfF7c2s,2972
|
|
33
|
-
vantage6/cli/context/node.py,sha256=
|
|
34
|
-
vantage6/cli/context/server.py,sha256=
|
|
25
|
+
vantage6/cli/context/node.py,sha256=bjrBGopoqR0SVTYvKCmhhGTzdaX8wg0GLBKbiLKMIGQ,7264
|
|
26
|
+
vantage6/cli/context/server.py,sha256=Bnv25avqIv0eG79uuY6W8ZrktTDAcF82Gmeo-dKjbyI,4630
|
|
35
27
|
vantage6/cli/dev/create.py,sha256=WBBPG9jyNvPQVb1koHX9y3VKQmlijlq-QBsYYzqqojg,21207
|
|
36
28
|
vantage6/cli/dev/remove.py,sha256=kjmOqkMO0ZU5F-3KgQ05VlfQQCS5Ta6yLY32DU0rH7k,3917
|
|
37
29
|
vantage6/cli/dev/data/km_dataset.csv,sha256=OrYF2ympb2QndiUOX_nTFGGB1HbAp2eOvZzrQ_PqJs4,31283
|
|
@@ -77,8 +69,7 @@ vantage6/cli/test/algo_test_scripts/algo_test_script.py,sha256=jfzXPmpL0HlE_eq1j
|
|
|
77
69
|
vantage6/cli/test/common/diagnostic_runner.py,sha256=5-KgspYW0PncO_t_TrQzd_GSOXVwH-7bIVv-IHOxwQM,6598
|
|
78
70
|
vantage6/cli/use/context.py,sha256=mEtOfbuLtYQlRh-ypif24WtOwgpcvXObX310mmXIkWY,1362
|
|
79
71
|
vantage6/cli/use/namespace.py,sha256=MOd9H3GJwZ0cho0mmlHTRlGPLoQEiSnZIFDsFsvYw4Q,1643
|
|
80
|
-
vantage6-5.0.
|
|
81
|
-
vantage6-5.0.
|
|
82
|
-
vantage6-5.0.
|
|
83
|
-
vantage6-5.0.
|
|
84
|
-
vantage6-5.0.0a29.dist-info/RECORD,,
|
|
72
|
+
vantage6-5.0.0a33.dist-info/METADATA,sha256=ZaeekTWCozYRPHpHPBGCxXRxuw9ovIRH_Ld-VvJX9gs,1779
|
|
73
|
+
vantage6-5.0.0a33.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
74
|
+
vantage6-5.0.0a33.dist-info/entry_points.txt,sha256=RKVCMsD70s_Gp6If89uDlCphsZ9uLIOMt1gciv8EMDQ,53
|
|
75
|
+
vantage6-5.0.0a33.dist-info/RECORD,,
|
tests_cli/__init__.py
DELETED
|
File without changes
|
tests_cli/test_client_script.py
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# from click import UsageError
|
|
2
|
-
# from vantage6.cli.test.client_script import cli_test_client_script
|
|
3
|
-
|
|
4
|
-
# import click
|
|
5
|
-
# import unittest
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
# class TestScriptTest(unittest.TestCase):
|
|
9
|
-
# def test_script_incorrect_usage(self):
|
|
10
|
-
# ctx = click.Context(cli_test_client_script)
|
|
11
|
-
|
|
12
|
-
# with self.assertRaises(UsageError):
|
|
13
|
-
# ctx.invoke(
|
|
14
|
-
# cli_test_client_script,
|
|
15
|
-
# script="path/to/script.py",
|
|
16
|
-
# task_arguments="{'my_arg': 1}",
|
|
17
|
-
# )
|
|
18
|
-
|
|
19
|
-
# with self.assertRaises(UsageError):
|
|
20
|
-
# ctx.invoke(
|
|
21
|
-
# cli_test_client_script,
|
|
22
|
-
# task_arguments="not_a_json",
|
|
23
|
-
# )
|
tests_cli/test_example.py
DELETED
tests_cli/test_node_cli.py
DELETED
|
@@ -1,452 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
import logging
|
|
3
|
-
import os
|
|
4
|
-
import contextlib
|
|
5
|
-
|
|
6
|
-
from unittest.mock import MagicMock, patch
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from io import BytesIO, StringIO
|
|
9
|
-
from click.testing import CliRunner
|
|
10
|
-
from docker.errors import APIError
|
|
11
|
-
|
|
12
|
-
from vantage6.common.globals import Ports
|
|
13
|
-
from vantage6.cli.globals import APPNAME
|
|
14
|
-
from vantage6.common import STRING_ENCODING
|
|
15
|
-
from vantage6.cli.common.utils import print_log_worker
|
|
16
|
-
from vantage6.cli.node.list import cli_node_list
|
|
17
|
-
from vantage6.cli.node.new import cli_node_new_configuration
|
|
18
|
-
from vantage6.cli.node.files import cli_node_files
|
|
19
|
-
from vantage6.cli.node.start import cli_node_start
|
|
20
|
-
from vantage6.cli.node.restart import cli_node_restart
|
|
21
|
-
from vantage6.cli.node.stop import cli_node_stop
|
|
22
|
-
from vantage6.cli.node.attach import cli_node_attach
|
|
23
|
-
from vantage6.cli.node.create_private_key import cli_node_create_private_key
|
|
24
|
-
from vantage6.cli.node.clean import cli_node_clean
|
|
25
|
-
from vantage6.cli.node.common import create_client_and_authenticate
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class NodeCLITest(unittest.TestCase):
|
|
29
|
-
@classmethod
|
|
30
|
-
def setUpClass(cls):
|
|
31
|
-
logging.getLogger("docker.utils.config").setLevel(logging.WARNING)
|
|
32
|
-
return super().setUpClass()
|
|
33
|
-
|
|
34
|
-
@patch("docker.DockerClient.ping")
|
|
35
|
-
def test_list_docker_not_running(self, docker_ping):
|
|
36
|
-
"""An error is printed when docker is not running"""
|
|
37
|
-
docker_ping.side_effect = Exception("Boom!")
|
|
38
|
-
|
|
39
|
-
runner = CliRunner()
|
|
40
|
-
result = runner.invoke(cli_node_list, [])
|
|
41
|
-
|
|
42
|
-
# check exit code
|
|
43
|
-
self.assertEqual(result.exit_code, 1)
|
|
44
|
-
|
|
45
|
-
@patch("vantage6.cli.context.node.NodeContext.available_configurations")
|
|
46
|
-
@patch("docker.DockerClient.ping")
|
|
47
|
-
@patch("docker.DockerClient.containers")
|
|
48
|
-
def test_list(self, containers, docker_ping, available_configurations):
|
|
49
|
-
"""A container list and their current status."""
|
|
50
|
-
# https://docs.python.org/3/library/unittest.mock.html#mock-names-and-the-name-attribute
|
|
51
|
-
|
|
52
|
-
# mock that docker-deamon is running
|
|
53
|
-
docker_ping.return_value = True
|
|
54
|
-
|
|
55
|
-
# docker deamon returns a list of running node-containers
|
|
56
|
-
container1 = MagicMock()
|
|
57
|
-
container1.name = f"{APPNAME}-iknl-user"
|
|
58
|
-
containers.list.return_value = [container1]
|
|
59
|
-
|
|
60
|
-
# returns a list of configurations and failed inports
|
|
61
|
-
def side_effect(system_folders):
|
|
62
|
-
config = MagicMock()
|
|
63
|
-
config.name = "iknl"
|
|
64
|
-
if not system_folders:
|
|
65
|
-
return [[config], []]
|
|
66
|
-
else:
|
|
67
|
-
return [[config], []]
|
|
68
|
-
|
|
69
|
-
available_configurations.side_effect = side_effect
|
|
70
|
-
|
|
71
|
-
# invoke CLI method
|
|
72
|
-
runner = CliRunner()
|
|
73
|
-
result = runner.invoke(cli_node_list, [])
|
|
74
|
-
|
|
75
|
-
# validate exit code
|
|
76
|
-
self.assertEqual(result.exit_code, 0)
|
|
77
|
-
|
|
78
|
-
# check printed lines
|
|
79
|
-
self.assertEqual(
|
|
80
|
-
result.output,
|
|
81
|
-
"\nName Status System/User\n"
|
|
82
|
-
"-----------------------------------------------------\n"
|
|
83
|
-
"iknl Not running System \n"
|
|
84
|
-
"iknl Running User \n"
|
|
85
|
-
"-----------------------------------------------------\n",
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
@patch("vantage6.cli.node.new.configuration_wizard")
|
|
89
|
-
@patch("vantage6.cli.node.new.ensure_config_dir_writable")
|
|
90
|
-
@patch("vantage6.cli.node.common.NodeContext")
|
|
91
|
-
def test_new_config(self, context, permissions, wizard):
|
|
92
|
-
"""No error produced when creating new configuration."""
|
|
93
|
-
context.config_exists.return_value = False
|
|
94
|
-
permissions.return_value = True
|
|
95
|
-
wizard.return_value = "/some/file/path"
|
|
96
|
-
|
|
97
|
-
runner = CliRunner()
|
|
98
|
-
result = runner.invoke(
|
|
99
|
-
cli_node_new_configuration,
|
|
100
|
-
[
|
|
101
|
-
"--name",
|
|
102
|
-
"some-name",
|
|
103
|
-
],
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
# check that info message is produced
|
|
107
|
-
self.assertEqual(result.output[:7], "[info ]")
|
|
108
|
-
|
|
109
|
-
# check OK exit code
|
|
110
|
-
self.assertEqual(result.exit_code, 0)
|
|
111
|
-
|
|
112
|
-
@patch("vantage6.cli.node.new.configuration_wizard")
|
|
113
|
-
def test_new_config_replace_whitespace_in_name(self, _):
|
|
114
|
-
"""Whitespaces are replaced in the name."""
|
|
115
|
-
|
|
116
|
-
runner = CliRunner()
|
|
117
|
-
result = runner.invoke(
|
|
118
|
-
cli_node_new_configuration,
|
|
119
|
-
[
|
|
120
|
-
"--name",
|
|
121
|
-
"some name",
|
|
122
|
-
],
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
self.assertEqual(
|
|
126
|
-
result.output[:60],
|
|
127
|
-
"[info ] - Replaced spaces from configuration name: some-name",
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
@patch("vantage6.cli.node.new.NodeContext")
|
|
131
|
-
def test_new_config_already_exists(self, context):
|
|
132
|
-
"""No duplicate configurations are allowed."""
|
|
133
|
-
|
|
134
|
-
context.config_exists.return_value = True
|
|
135
|
-
|
|
136
|
-
runner = CliRunner()
|
|
137
|
-
result = runner.invoke(
|
|
138
|
-
cli_node_new_configuration,
|
|
139
|
-
[
|
|
140
|
-
"--name",
|
|
141
|
-
"some-name",
|
|
142
|
-
],
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
# check that error is produced
|
|
146
|
-
self.assertEqual(result.output[:7], "[error]")
|
|
147
|
-
|
|
148
|
-
# check non-zero exit code
|
|
149
|
-
self.assertEqual(result.exit_code, 1)
|
|
150
|
-
|
|
151
|
-
@patch("vantage6.cli.node.new.ensure_config_dir_writable")
|
|
152
|
-
@patch("vantage6.cli.node.common.NodeContext")
|
|
153
|
-
def test_new_write_permissions(self, context, permissions):
|
|
154
|
-
"""User needs write permissions."""
|
|
155
|
-
|
|
156
|
-
context.config_exists.return_value = False
|
|
157
|
-
permissions.return_value = False
|
|
158
|
-
|
|
159
|
-
runner = CliRunner()
|
|
160
|
-
result = runner.invoke(
|
|
161
|
-
cli_node_new_configuration,
|
|
162
|
-
[
|
|
163
|
-
"--name",
|
|
164
|
-
"some-name",
|
|
165
|
-
],
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
# check that error is produced
|
|
169
|
-
self.assertEqual(result.output[:7], "[error]")
|
|
170
|
-
|
|
171
|
-
# check non-zero exit code
|
|
172
|
-
self.assertEqual(result.exit_code, 1)
|
|
173
|
-
|
|
174
|
-
@patch("vantage6.cli.node.common.NodeContext")
|
|
175
|
-
@patch("vantage6.cli.node.files.NodeContext")
|
|
176
|
-
@patch("vantage6.cli.node.common.select_configuration_questionaire")
|
|
177
|
-
def test_files(self, select_config, context, common_context):
|
|
178
|
-
"""No errors produced when retrieving filepaths."""
|
|
179
|
-
|
|
180
|
-
common_context.config_exists.return_value = True
|
|
181
|
-
context.return_value = MagicMock(
|
|
182
|
-
config_file="/file.yaml", log_file="/log.log", data_dir="/dir"
|
|
183
|
-
)
|
|
184
|
-
context.return_value.databases.items.return_value = [["label", "/file.db"]]
|
|
185
|
-
select_config.return_value = "iknl"
|
|
186
|
-
|
|
187
|
-
runner = CliRunner()
|
|
188
|
-
result = runner.invoke(cli_node_files, [])
|
|
189
|
-
|
|
190
|
-
# we check that no warnings have been produced
|
|
191
|
-
self.assertEqual(result.output[:7], "[info ]")
|
|
192
|
-
|
|
193
|
-
# check status code is OK
|
|
194
|
-
self.assertEqual(result.exit_code, 0)
|
|
195
|
-
|
|
196
|
-
@patch("vantage6.cli.node.common.NodeContext")
|
|
197
|
-
def test_files_non_existing_config(self, context):
|
|
198
|
-
"""An error is produced when a non existing config is used."""
|
|
199
|
-
|
|
200
|
-
context.config_exists.return_value = False
|
|
201
|
-
|
|
202
|
-
runner = CliRunner()
|
|
203
|
-
result = runner.invoke(cli_node_files, ["--name", "non-existing"])
|
|
204
|
-
|
|
205
|
-
# Check that error is produced
|
|
206
|
-
self.assertEqual(result.output[:7], "[error]")
|
|
207
|
-
|
|
208
|
-
# check for non zero exit-code
|
|
209
|
-
self.assertNotEqual(result.exit_code, 0)
|
|
210
|
-
|
|
211
|
-
@patch("docker.DockerClient.volumes")
|
|
212
|
-
@patch("vantage6.cli.node.start.pull_infra_image")
|
|
213
|
-
@patch("vantage6.cli.common.decorator.get_context")
|
|
214
|
-
@patch("docker.DockerClient.containers")
|
|
215
|
-
@patch("vantage6.cli.node.start.check_docker_running", return_value=True)
|
|
216
|
-
def test_start(self, check_docker, client, context, pull, volumes):
|
|
217
|
-
# client.containers = MagicMock(name="docker.DockerClient.containers")
|
|
218
|
-
client.list.return_value = []
|
|
219
|
-
volume = MagicMock()
|
|
220
|
-
volume.name = "data-vol-name"
|
|
221
|
-
volumes.create.return_value = volume
|
|
222
|
-
context.config_exists.return_value = True
|
|
223
|
-
|
|
224
|
-
ctx = MagicMock(
|
|
225
|
-
data_dir=Path("data"),
|
|
226
|
-
log_dir=Path("logs"),
|
|
227
|
-
config_dir=Path("configs"),
|
|
228
|
-
databases=[{"label": "some_label", "uri": "data.csv", "type": "csv"}],
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
# cli_node_start() tests for truth value of a set-like object derived
|
|
232
|
-
# from ctx.config.get('node_extra_env', {}). Default MagicMock() will
|
|
233
|
-
# evaluate to True, empty dict to False. False signifies no overwritten
|
|
234
|
-
# env vars, hence no error.
|
|
235
|
-
def config_get_side_effect(key, default=None):
|
|
236
|
-
if key == "node_extra_env":
|
|
237
|
-
return {}
|
|
238
|
-
return MagicMock()
|
|
239
|
-
|
|
240
|
-
ctx.config.get.side_effect = config_get_side_effect
|
|
241
|
-
ctx.get_data_file.return_value = "data.csv"
|
|
242
|
-
ctx.name = "some-name"
|
|
243
|
-
context.return_value = ctx
|
|
244
|
-
|
|
245
|
-
runner = CliRunner()
|
|
246
|
-
|
|
247
|
-
# Should fail when starting node with non-existing database CSV file
|
|
248
|
-
with runner.isolated_filesystem():
|
|
249
|
-
result = runner.invoke(cli_node_start, ["--name", "some-name"])
|
|
250
|
-
self.assertEqual(result.exit_code, 1)
|
|
251
|
-
|
|
252
|
-
# now do it with a SQL database which doesn't have to be an existing file
|
|
253
|
-
ctx.databases = [{"label": "some_label", "uri": "data.db", "type": "sql"}]
|
|
254
|
-
with runner.isolated_filesystem():
|
|
255
|
-
result = runner.invoke(cli_node_start, ["--name", "some-name"])
|
|
256
|
-
self.assertEqual(result.exit_code, 0)
|
|
257
|
-
|
|
258
|
-
def _setup_stop_test(self, containers):
|
|
259
|
-
container1 = MagicMock()
|
|
260
|
-
container1.name = f"{APPNAME}-iknl-user"
|
|
261
|
-
containers.list.return_value = [container1]
|
|
262
|
-
|
|
263
|
-
@patch("docker.DockerClient.containers")
|
|
264
|
-
@patch("vantage6.cli.node.stop.check_docker_running", return_value=True)
|
|
265
|
-
@patch("vantage6.cli.node.stop.NodeContext")
|
|
266
|
-
@patch("vantage6.cli.node.stop.delete_volume_if_exists")
|
|
267
|
-
def test_stop(self, delete_volume, node_context, check_docker, containers):
|
|
268
|
-
self._setup_stop_test(containers)
|
|
269
|
-
|
|
270
|
-
runner = CliRunner()
|
|
271
|
-
|
|
272
|
-
result = runner.invoke(cli_node_stop, ["--name", "iknl"])
|
|
273
|
-
|
|
274
|
-
self.assertEqual(
|
|
275
|
-
result.output, "[info ] - Stopped the vantage6-iknl-user Node.\n"
|
|
276
|
-
)
|
|
277
|
-
|
|
278
|
-
self.assertEqual(result.exit_code, 0)
|
|
279
|
-
|
|
280
|
-
@patch("docker.DockerClient.containers")
|
|
281
|
-
@patch("vantage6.cli.node.stop.check_docker_running", return_value=True)
|
|
282
|
-
@patch("vantage6.cli.node.stop.NodeContext")
|
|
283
|
-
@patch("vantage6.cli.node.stop.delete_volume_if_exists")
|
|
284
|
-
@patch("vantage6.cli.node.restart.subprocess.run")
|
|
285
|
-
def test_restart(
|
|
286
|
-
self, subprocess_run, delete_volume, node_context, check_docker, containers
|
|
287
|
-
):
|
|
288
|
-
"""Restart a node without errors."""
|
|
289
|
-
self._setup_stop_test(containers)
|
|
290
|
-
# The subprocess.run() function is called with the command to start the node.
|
|
291
|
-
# Unfortunately it is hard to test this, so we just return a successful run
|
|
292
|
-
subprocess_run.return_value = MagicMock(returncode=0)
|
|
293
|
-
runner = CliRunner()
|
|
294
|
-
with runner.isolated_filesystem():
|
|
295
|
-
result = runner.invoke(cli_node_restart, ["--name", "iknl"])
|
|
296
|
-
self.assertEqual(result.exit_code, 0)
|
|
297
|
-
|
|
298
|
-
@patch("vantage6.cli.node.attach.attach_logs")
|
|
299
|
-
def test_attach(self, attach_logs):
|
|
300
|
-
"""Attach docker logs without errors."""
|
|
301
|
-
runner = CliRunner()
|
|
302
|
-
runner.invoke(cli_node_attach)
|
|
303
|
-
attach_logs.assert_called_once_with("app=node")
|
|
304
|
-
|
|
305
|
-
@patch("vantage6.cli.node.clean.q")
|
|
306
|
-
@patch("docker.DockerClient.volumes")
|
|
307
|
-
@patch("vantage6.cli.node.clean.check_docker_running", return_value=True)
|
|
308
|
-
def test_clean(self, check_docker, volumes, q):
|
|
309
|
-
"""Clean Docker volumes without errors."""
|
|
310
|
-
volume1 = MagicMock()
|
|
311
|
-
volume1.name = "some-name-tmpvol"
|
|
312
|
-
volumes.list.return_value = [volume1]
|
|
313
|
-
|
|
314
|
-
question = MagicMock(name="pop-the-question")
|
|
315
|
-
question.ask.return_value = True
|
|
316
|
-
q.confirm.return_value = question
|
|
317
|
-
|
|
318
|
-
runner = CliRunner()
|
|
319
|
-
result = runner.invoke(cli_node_clean)
|
|
320
|
-
|
|
321
|
-
# check exit code
|
|
322
|
-
self.assertEqual(result.exit_code, 0)
|
|
323
|
-
|
|
324
|
-
@patch("vantage6.cli.node.create_private_key.create_client_and_authenticate")
|
|
325
|
-
@patch("vantage6.cli.node.common.NodeContext")
|
|
326
|
-
@patch("vantage6.cli.node.create_private_key.NodeContext")
|
|
327
|
-
def test_create_private_key(self, context, common_context, client):
|
|
328
|
-
common_context.config_exists.return_value = True
|
|
329
|
-
context.return_value.type_data_folder.return_value = Path(".")
|
|
330
|
-
client.return_value = MagicMock(whoami=MagicMock(organization_name="Test"))
|
|
331
|
-
# client.whoami.organization_name = "Test"
|
|
332
|
-
runner = CliRunner()
|
|
333
|
-
|
|
334
|
-
result = runner.invoke(cli_node_create_private_key, ["--name", "application"])
|
|
335
|
-
|
|
336
|
-
self.assertEqual(result.exit_code, 0)
|
|
337
|
-
|
|
338
|
-
# remove the private key file again
|
|
339
|
-
os.remove("privkey_Test.pem")
|
|
340
|
-
|
|
341
|
-
@patch("vantage6.cli.node.create_private_key.RSACryptor")
|
|
342
|
-
@patch("vantage6.cli.node.create_private_key.create_client_and_authenticate")
|
|
343
|
-
@patch("vantage6.cli.node.common.NodeContext")
|
|
344
|
-
@patch("vantage6.cli.node.create_private_key.NodeContext")
|
|
345
|
-
def test_create_private_key_overwite(
|
|
346
|
-
self, context, common_context, client, cryptor
|
|
347
|
-
):
|
|
348
|
-
common_context.config_exists.return_value = True
|
|
349
|
-
context.return_value.type_data_folder.return_value = Path(".")
|
|
350
|
-
client.return_value = MagicMock(whoami=MagicMock(organization_name="Test"))
|
|
351
|
-
cryptor.create_public_key_bytes.return_value = b""
|
|
352
|
-
# client.whoami.organization_name = "Test"
|
|
353
|
-
|
|
354
|
-
runner = CliRunner()
|
|
355
|
-
|
|
356
|
-
# overwrite
|
|
357
|
-
with runner.isolated_filesystem():
|
|
358
|
-
with open("privkey_iknl.pem", "w") as f:
|
|
359
|
-
f.write("does-not-matter")
|
|
360
|
-
|
|
361
|
-
result = runner.invoke(
|
|
362
|
-
cli_node_create_private_key,
|
|
363
|
-
["--name", "application", "--overwrite", "--organization-name", "iknl"],
|
|
364
|
-
)
|
|
365
|
-
self.assertEqual(result.exit_code, 0)
|
|
366
|
-
|
|
367
|
-
# do not overwrite
|
|
368
|
-
with runner.isolated_filesystem():
|
|
369
|
-
with open("privkey_iknl.pem", "w") as f:
|
|
370
|
-
f.write("does-not-matter")
|
|
371
|
-
|
|
372
|
-
result = runner.invoke(
|
|
373
|
-
cli_node_create_private_key,
|
|
374
|
-
["--name", "application", "--organization-name", "iknl"],
|
|
375
|
-
)
|
|
376
|
-
|
|
377
|
-
# print(result.output)
|
|
378
|
-
|
|
379
|
-
self.assertEqual(result.exit_code, 0)
|
|
380
|
-
|
|
381
|
-
@patch("vantage6.cli.node.common.NodeContext")
|
|
382
|
-
def test_create_private_key_config_not_found(self, context):
|
|
383
|
-
context.config_exists.return_value = False
|
|
384
|
-
|
|
385
|
-
runner = CliRunner()
|
|
386
|
-
result = runner.invoke(cli_node_create_private_key, ["--name", "application"])
|
|
387
|
-
|
|
388
|
-
self.assertEqual(result.exit_code, 1)
|
|
389
|
-
|
|
390
|
-
@patch("vantage6.cli.node.clean.q")
|
|
391
|
-
@patch("docker.DockerClient.volumes")
|
|
392
|
-
@patch("vantage6.common.docker.addons.check_docker_running")
|
|
393
|
-
def test_clean_docker_error(self, check_docker, volumes, q):
|
|
394
|
-
volume1 = MagicMock()
|
|
395
|
-
volume1.name = "some-name-tmpvol"
|
|
396
|
-
volume1.remove.side_effect = APIError("Testing")
|
|
397
|
-
volumes.list.return_value = [volume1]
|
|
398
|
-
question = MagicMock(name="pop-the-question")
|
|
399
|
-
question.ask.return_value = True
|
|
400
|
-
q.confirm.return_value = question
|
|
401
|
-
|
|
402
|
-
runner = CliRunner()
|
|
403
|
-
result = runner.invoke(cli_node_clean)
|
|
404
|
-
|
|
405
|
-
# check exit code
|
|
406
|
-
self.assertEqual(result.exit_code, 1)
|
|
407
|
-
|
|
408
|
-
def test_print_log_worker(self):
|
|
409
|
-
stream = BytesIO("Hello!".encode(STRING_ENCODING))
|
|
410
|
-
temp_stdout = StringIO()
|
|
411
|
-
with contextlib.redirect_stdout(temp_stdout):
|
|
412
|
-
print_log_worker(stream)
|
|
413
|
-
output = temp_stdout.getvalue().strip()
|
|
414
|
-
self.assertEqual(output, "Hello!")
|
|
415
|
-
|
|
416
|
-
@patch("vantage6.cli.node.common.info")
|
|
417
|
-
@patch("vantage6.cli.node.common.debug")
|
|
418
|
-
@patch("vantage6.cli.node.common.error")
|
|
419
|
-
@patch("vantage6.cli.node.common.UserClient")
|
|
420
|
-
def test_client(self, client, error, debug, info):
|
|
421
|
-
ctx = MagicMock(
|
|
422
|
-
config={
|
|
423
|
-
"server_url": "localhost",
|
|
424
|
-
"port": Ports.DEV_SERVER.value,
|
|
425
|
-
"api_path": "",
|
|
426
|
-
}
|
|
427
|
-
)
|
|
428
|
-
|
|
429
|
-
# should not trigger an exception
|
|
430
|
-
try:
|
|
431
|
-
create_client_and_authenticate(ctx)
|
|
432
|
-
except Exception:
|
|
433
|
-
self.fail("Raised an exception!")
|
|
434
|
-
|
|
435
|
-
# client raises exception
|
|
436
|
-
client.side_effect = Exception("Boom!")
|
|
437
|
-
with self.assertRaises(Exception):
|
|
438
|
-
create_client_and_authenticate(ctx)
|
|
439
|
-
|
|
440
|
-
# TODO this function has been moved to the common package. A test should
|
|
441
|
-
# be added there instead of here
|
|
442
|
-
# @patch("vantage6.cli.node.error")
|
|
443
|
-
# def test_check_docker(self, error):
|
|
444
|
-
# docker = MagicMock()
|
|
445
|
-
# try:
|
|
446
|
-
# check_docker_running()
|
|
447
|
-
# except Exception:
|
|
448
|
-
# self.fail("Exception raised!")
|
|
449
|
-
|
|
450
|
-
# docker.ping.side_effect = Exception("Boom!")
|
|
451
|
-
# with self.assertRaises(SystemExit):
|
|
452
|
-
# check_docker_running()
|
tests_cli/test_server_cli.py
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
from unittest.mock import MagicMock, patch
|
|
4
|
-
|
|
5
|
-
from click.testing import CliRunner
|
|
6
|
-
|
|
7
|
-
from vantage6.common.globals import APPNAME, InstanceType
|
|
8
|
-
|
|
9
|
-
from vantage6.cli.common.utils import attach_logs
|
|
10
|
-
from vantage6.cli.server.attach import cli_server_attach
|
|
11
|
-
from vantage6.cli.server.files import cli_server_files
|
|
12
|
-
from vantage6.cli.server.import_ import cli_server_import
|
|
13
|
-
from vantage6.cli.server.list import cli_server_configuration_list
|
|
14
|
-
from vantage6.cli.server.new import cli_server_new
|
|
15
|
-
from vantage6.cli.server.start import cli_server_start
|
|
16
|
-
from vantage6.cli.server.stop import cli_server_stop
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class ServerCLITest(unittest.TestCase):
|
|
20
|
-
@patch("vantage6.cli.server.start.NetworkManager")
|
|
21
|
-
@patch("vantage6.cli.server.start.docker.types.Mount")
|
|
22
|
-
@patch("os.makedirs")
|
|
23
|
-
@patch("vantage6.cli.server.start.pull_infra_image")
|
|
24
|
-
@patch("vantage6.cli.common.decorator.get_context")
|
|
25
|
-
@patch("vantage6.cli.server.start.docker.from_env")
|
|
26
|
-
@patch("vantage6.cli.common.start.check_docker_running", return_value=True)
|
|
27
|
-
def test_start(
|
|
28
|
-
self,
|
|
29
|
-
docker_check,
|
|
30
|
-
containers,
|
|
31
|
-
context,
|
|
32
|
-
pull,
|
|
33
|
-
os_makedirs,
|
|
34
|
-
mount,
|
|
35
|
-
network_manager,
|
|
36
|
-
):
|
|
37
|
-
"""Start server without errors"""
|
|
38
|
-
container1 = MagicMock()
|
|
39
|
-
container1.containers.name = f"{APPNAME}-iknl-system"
|
|
40
|
-
containers.containers.list.return_value = [container1]
|
|
41
|
-
containers.containers.run.return_value = True
|
|
42
|
-
|
|
43
|
-
# mount.types.Mount.return_value = MagicMock()
|
|
44
|
-
|
|
45
|
-
ctx = MagicMock(
|
|
46
|
-
config={"uri": "sqlite:///file.db", "port": 9999},
|
|
47
|
-
config_file="/config.yaml",
|
|
48
|
-
data_dir=Path("."),
|
|
49
|
-
)
|
|
50
|
-
ctx.config_exists.return_value = True
|
|
51
|
-
ctx.name = "not-running"
|
|
52
|
-
context.return_value = ctx
|
|
53
|
-
|
|
54
|
-
runner = CliRunner()
|
|
55
|
-
result = runner.invoke(cli_server_start, ["--name", "not-running"])
|
|
56
|
-
|
|
57
|
-
self.assertEqual(result.exit_code, 0)
|
|
58
|
-
|
|
59
|
-
@patch("vantage6.cli.server.common.ServerContext")
|
|
60
|
-
@patch("docker.DockerClient.containers")
|
|
61
|
-
@patch("vantage6.cli.server.list.check_docker_running", return_value=True)
|
|
62
|
-
def test_configuration_list(self, docker_check, containers, context):
|
|
63
|
-
"""Configuration list without errors."""
|
|
64
|
-
container1 = MagicMock()
|
|
65
|
-
container1.name = f"{APPNAME}-iknl-system"
|
|
66
|
-
containers.list.return_value = [container1]
|
|
67
|
-
|
|
68
|
-
config = MagicMock()
|
|
69
|
-
config.name = "iknl"
|
|
70
|
-
context.available_configurations.return_value = ([config], [])
|
|
71
|
-
|
|
72
|
-
runner = CliRunner()
|
|
73
|
-
result = runner.invoke(cli_server_configuration_list)
|
|
74
|
-
|
|
75
|
-
self.assertEqual(result.exit_code, 0)
|
|
76
|
-
self.assertIsNone(result.exception)
|
|
77
|
-
|
|
78
|
-
@patch("vantage6.cli.common.decorator.get_context")
|
|
79
|
-
def test_files(self, context):
|
|
80
|
-
"""Configuration files without errors."""
|
|
81
|
-
|
|
82
|
-
ctx = context.return_value = MagicMock(
|
|
83
|
-
log_file="/log_file.log", config_file="/iknl.yaml"
|
|
84
|
-
)
|
|
85
|
-
ctx.get_database_uri.return_value = "sqlite:///test.db"
|
|
86
|
-
|
|
87
|
-
runner = CliRunner()
|
|
88
|
-
result = runner.invoke(cli_server_files, ["--name", "iknl"])
|
|
89
|
-
|
|
90
|
-
self.assertIsNone(result.exception)
|
|
91
|
-
self.assertEqual(result.exit_code, 0)
|
|
92
|
-
|
|
93
|
-
@patch("docker.DockerClient.containers")
|
|
94
|
-
@patch("vantage6.cli.server.import_.print_log_worker")
|
|
95
|
-
@patch("vantage6.cli.server.import_.click.Path")
|
|
96
|
-
@patch("vantage6.cli.server.import_.pull_image")
|
|
97
|
-
@patch("vantage6.cli.server.import_.check_docker_running", return_value=True)
|
|
98
|
-
@patch("vantage6.cli.common.decorator.get_context")
|
|
99
|
-
def test_import(self, context, docker_check, pull, click_path, log, containers):
|
|
100
|
-
"""Import entities without errors."""
|
|
101
|
-
click_path.return_value = MagicMock()
|
|
102
|
-
|
|
103
|
-
ctx = MagicMock()
|
|
104
|
-
ctx.name = "some-name"
|
|
105
|
-
context.return_value = ctx
|
|
106
|
-
|
|
107
|
-
runner = CliRunner()
|
|
108
|
-
with runner.isolated_filesystem():
|
|
109
|
-
with open("some.yaml", "w") as fp:
|
|
110
|
-
fp.write("does-not-matter")
|
|
111
|
-
result = runner.invoke(cli_server_import, ["--name", "iknl", "some.yaml"])
|
|
112
|
-
|
|
113
|
-
self.assertIsNone(result.exception)
|
|
114
|
-
self.assertEqual(result.exit_code, 0)
|
|
115
|
-
|
|
116
|
-
@patch("vantage6.cli.server.new.configuration_wizard")
|
|
117
|
-
@patch("vantage6.cli.server.new.ensure_config_dir_writable")
|
|
118
|
-
@patch("vantage6.cli.server.new.ServerContext")
|
|
119
|
-
def test_new(self, context, permissions, wizard):
|
|
120
|
-
"""New configuration without errors."""
|
|
121
|
-
|
|
122
|
-
context.config_exists.return_value = False
|
|
123
|
-
permissions.return_value = True
|
|
124
|
-
wizard.return_value = "/some/file.yaml"
|
|
125
|
-
|
|
126
|
-
runner = CliRunner()
|
|
127
|
-
result = runner.invoke(cli_server_new, ["--name", "iknl"])
|
|
128
|
-
|
|
129
|
-
self.assertIsNone(result.exception)
|
|
130
|
-
self.assertEqual(result.exit_code, 0)
|
|
131
|
-
|
|
132
|
-
@patch("vantage6.cli.server.stop.docker.from_env")
|
|
133
|
-
def test_stop(self, containers):
|
|
134
|
-
"""Stop server without errors."""
|
|
135
|
-
|
|
136
|
-
container1 = MagicMock()
|
|
137
|
-
container1.name = f"{APPNAME}-iknl-system-{InstanceType.SERVER}"
|
|
138
|
-
containers.containers.list.return_value = [container1]
|
|
139
|
-
|
|
140
|
-
runner = CliRunner()
|
|
141
|
-
result = runner.invoke(cli_server_stop, ["--name", "iknl"])
|
|
142
|
-
|
|
143
|
-
self.assertIsNone(result.exception)
|
|
144
|
-
self.assertEqual(result.exit_code, 0)
|
|
145
|
-
|
|
146
|
-
@patch("vantage6.cli.server.attach.attach_logs")
|
|
147
|
-
def test_attach(self, attach_logs):
|
|
148
|
-
"""Attach logs to the console without errors."""
|
|
149
|
-
runner = CliRunner()
|
|
150
|
-
result = runner.invoke(cli_server_attach)
|
|
151
|
-
|
|
152
|
-
self.assertIsNone(result.exception)
|
|
153
|
-
self.assertEqual(result.exit_code, 0)
|
|
154
|
-
attach_logs.assert_called_once_with(
|
|
155
|
-
"app=vantage6-server", "component=vantage6-server"
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
@patch("vantage6.cli.common.utils.Popen")
|
|
159
|
-
def test_attach_logs(self, mock_popen):
|
|
160
|
-
# Mock the Popen instance and its methods
|
|
161
|
-
mock_process = mock_popen.return_value
|
|
162
|
-
mock_process.wait.return_value = None
|
|
163
|
-
|
|
164
|
-
# Call the function with a sample label
|
|
165
|
-
attach_logs("app=node", "env=dev")
|
|
166
|
-
|
|
167
|
-
# Construct the expected command
|
|
168
|
-
expected_command = [
|
|
169
|
-
"devspace",
|
|
170
|
-
"logs",
|
|
171
|
-
"--follow",
|
|
172
|
-
"--label-selector",
|
|
173
|
-
"app=node,env=dev",
|
|
174
|
-
]
|
|
175
|
-
|
|
176
|
-
# Verify that Popen was called with the expected command
|
|
177
|
-
mock_popen.assert_called_once_with(expected_command, stdout=None, stderr=None)
|
|
178
|
-
|
|
179
|
-
# Verify that wait was called on the process
|
|
180
|
-
mock_process.wait.assert_called_once()
|
tests_cli/test_wizard.py
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
from unittest.mock import MagicMock, patch
|
|
4
|
-
|
|
5
|
-
from vantage6.common.globals import InstanceType, NodePolicy
|
|
6
|
-
|
|
7
|
-
from vantage6.cli.configuration_wizard import (
|
|
8
|
-
configuration_wizard,
|
|
9
|
-
node_configuration_questionaire,
|
|
10
|
-
select_configuration_questionaire,
|
|
11
|
-
server_configuration_questionaire,
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
module_path = "vantage6.cli.configuration_wizard"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class WizardTest(unittest.TestCase):
|
|
18
|
-
@staticmethod
|
|
19
|
-
def prompts(*args, **kwargs):
|
|
20
|
-
result = {}
|
|
21
|
-
for arg in args[0]:
|
|
22
|
-
name = arg["name"]
|
|
23
|
-
if name == "default": # default db path
|
|
24
|
-
result[name] = "/some/path/db.sqlite"
|
|
25
|
-
else:
|
|
26
|
-
if "default" in arg:
|
|
27
|
-
result[name] = arg["default"]
|
|
28
|
-
else:
|
|
29
|
-
result[name] = None
|
|
30
|
-
return result
|
|
31
|
-
|
|
32
|
-
@patch("vantage6.cli.configuration_wizard.NodeClient.authenticate")
|
|
33
|
-
def test_node_wizard(self, authenticate):
|
|
34
|
-
"""An error is printed when docker is not running"""
|
|
35
|
-
authenticate.return_value = None
|
|
36
|
-
|
|
37
|
-
with patch(f"{module_path}.q") as q:
|
|
38
|
-
q.unsafe_prompt.side_effect = self.prompts
|
|
39
|
-
q.confirm.return_value.unsafe_ask.side_effect = [
|
|
40
|
-
True, # add a database
|
|
41
|
-
False, # don't enable two-factor authentication
|
|
42
|
-
True, # add VPN server
|
|
43
|
-
True, # add algorithm policies
|
|
44
|
-
True, # add single algorithms to allowed_algorithms
|
|
45
|
-
"some-image", # algorithm image to whitelist
|
|
46
|
-
False, # don't add another algorithm image
|
|
47
|
-
True, # add algorithm stores to allowed_algorithm_stores
|
|
48
|
-
"some-store", # algorithm store to whitelist
|
|
49
|
-
False, # don't add another algorithm store
|
|
50
|
-
False, # answer question on combining policies on store level and
|
|
51
|
-
# single algorithm level
|
|
52
|
-
False, # don't abort if no server connection is made to pull
|
|
53
|
-
# collaboration settings
|
|
54
|
-
True, # Enable encryption
|
|
55
|
-
]
|
|
56
|
-
dirs = MagicMock(data="/")
|
|
57
|
-
config = node_configuration_questionaire(dirs, "iknl")
|
|
58
|
-
|
|
59
|
-
keys = [
|
|
60
|
-
"api_key",
|
|
61
|
-
"server_url",
|
|
62
|
-
"port",
|
|
63
|
-
"api_path",
|
|
64
|
-
"task_dir",
|
|
65
|
-
"databases",
|
|
66
|
-
"logging",
|
|
67
|
-
"encryption",
|
|
68
|
-
"vpn_subnet",
|
|
69
|
-
]
|
|
70
|
-
for key in keys:
|
|
71
|
-
self.assertIn(key, config)
|
|
72
|
-
nested_keys = [
|
|
73
|
-
["policies", NodePolicy.ALLOWED_ALGORITHMS.value],
|
|
74
|
-
["policies", NodePolicy.ALLOWED_ALGORITHM_STORES.value],
|
|
75
|
-
]
|
|
76
|
-
for nesting in nested_keys:
|
|
77
|
-
current_config = config
|
|
78
|
-
for key in nesting:
|
|
79
|
-
self.assertIn(key, current_config)
|
|
80
|
-
current_config = current_config[key]
|
|
81
|
-
|
|
82
|
-
def test_server_wizard(self):
|
|
83
|
-
with patch(f"{module_path}.q") as q:
|
|
84
|
-
q.unsafe_prompt.side_effect = self.prompts
|
|
85
|
-
q.confirm.return_value.unsafe_ask.side_effect = [
|
|
86
|
-
True,
|
|
87
|
-
True,
|
|
88
|
-
True,
|
|
89
|
-
True,
|
|
90
|
-
True,
|
|
91
|
-
True,
|
|
92
|
-
False,
|
|
93
|
-
]
|
|
94
|
-
|
|
95
|
-
config = server_configuration_questionaire("vantage6")
|
|
96
|
-
|
|
97
|
-
keys = [
|
|
98
|
-
"description",
|
|
99
|
-
"ip",
|
|
100
|
-
"port",
|
|
101
|
-
"api_path",
|
|
102
|
-
"uri",
|
|
103
|
-
"allow_drop_all",
|
|
104
|
-
"logging",
|
|
105
|
-
"vpn_server",
|
|
106
|
-
"rabbitmq",
|
|
107
|
-
"two_factor_auth",
|
|
108
|
-
"algorithm_stores",
|
|
109
|
-
]
|
|
110
|
-
|
|
111
|
-
for key in keys:
|
|
112
|
-
self.assertIn(key, config)
|
|
113
|
-
|
|
114
|
-
@patch(f"{module_path}.node_configuration_questionaire")
|
|
115
|
-
@patch(f"{module_path}.server_configuration_questionaire")
|
|
116
|
-
@patch(f"{module_path}.ServerConfigurationManager")
|
|
117
|
-
@patch(f"{module_path}.NodeConfigurationManager")
|
|
118
|
-
@patch("vantage6.cli.configuration_wizard.AppContext")
|
|
119
|
-
def test_configuration_wizard_interface(
|
|
120
|
-
self, context, node_m, server_m, server_q, node_q
|
|
121
|
-
):
|
|
122
|
-
context.instance_folders.return_value = {"config": "/some/path/"}
|
|
123
|
-
|
|
124
|
-
file_ = configuration_wizard(InstanceType.NODE, "vtg6", False)
|
|
125
|
-
self.assertEqual(Path("/some/path/vtg6.yaml"), file_)
|
|
126
|
-
|
|
127
|
-
file_ = configuration_wizard(InstanceType.SERVER, "vtg6", True)
|
|
128
|
-
self.assertEqual(Path("/some/path/vtg6.yaml"), file_)
|
|
129
|
-
|
|
130
|
-
@patch("vantage6.cli.configuration_wizard.AppContext.available_configurations")
|
|
131
|
-
def test_select_configuration(self, available_configurations):
|
|
132
|
-
config = MagicMock()
|
|
133
|
-
config.name = "vtg6"
|
|
134
|
-
|
|
135
|
-
available_configurations.return_value = [[config], []]
|
|
136
|
-
|
|
137
|
-
with patch(f"{module_path}.q") as q:
|
|
138
|
-
q.select.return_value.unsafe_ask.return_value = "vtg6"
|
|
139
|
-
name = select_configuration_questionaire(InstanceType.NODE, True)
|
|
140
|
-
|
|
141
|
-
self.assertEqual(name, "vtg6")
|
vantage6/cli/__build__
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
29
|
vantage6/cli/_version.py
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import json
|
|
3
|
-
|
|
4
|
-
here = os.path.abspath(os.path.dirname(__file__))
|
|
5
|
-
|
|
6
|
-
with open(os.path.join(here, "__build__")) as fp:
|
|
7
|
-
__build__ = json.load(fp)
|
|
8
|
-
|
|
9
|
-
# Module version
|
|
10
|
-
version_info = (5, 0, 0, "alpha", __build__, 0)
|
|
11
|
-
|
|
12
|
-
# Module version stage suffix map
|
|
13
|
-
_specifier_ = {"alpha": "a", "beta": "b", "candidate": "rc", "final": ""}
|
|
14
|
-
version = f"{version_info[0]}.{version_info[1]}.{version_info[2]}"
|
|
15
|
-
pre_release = (
|
|
16
|
-
""
|
|
17
|
-
if version_info[3] == "final"
|
|
18
|
-
else _specifier_[version_info[3]] + str(version_info[4])
|
|
19
|
-
)
|
|
20
|
-
post_release = "" if not version_info[5] else f".post{version_info[5]}"
|
|
21
|
-
|
|
22
|
-
# Module version accessible using thomas.__version__
|
|
23
|
-
__version__ = f"{version}{pre_release}{post_release}"
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: vantage6
|
|
3
|
-
Version: 5.0.0a29
|
|
4
|
-
Summary: vantage6 command line interface
|
|
5
|
-
Home-page: https://github.com/vantage6/vantage6
|
|
6
|
-
Requires-Python: >=3.13
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
|
-
Requires-Dist: click==8.1.3
|
|
9
|
-
Requires-Dist: colorama==0.4.6
|
|
10
|
-
Requires-Dist: copier==9.2.0
|
|
11
|
-
Requires-Dist: docker==7.1.0
|
|
12
|
-
Requires-Dist: ipython==8.10.0
|
|
13
|
-
Requires-Dist: jinja2==3.1.6
|
|
14
|
-
Requires-Dist: kubernetes==28.1.0
|
|
15
|
-
Requires-Dist: pandas>=2.2.3
|
|
16
|
-
Requires-Dist: questionary==1.10.0
|
|
17
|
-
Requires-Dist: rich==13.5.2
|
|
18
|
-
Requires-Dist: schema==0.7.5
|
|
19
|
-
Requires-Dist: sqlalchemy==2.0.37
|
|
20
|
-
Requires-Dist: vantage6-common==5.0.0a29
|
|
21
|
-
Requires-Dist: vantage6-client==5.0.0a29
|
|
22
|
-
Provides-Extra: dev
|
|
23
|
-
Requires-Dist: coverage==6.4.4; extra == "dev"
|
|
24
|
-
Requires-Dist: black; extra == "dev"
|
|
25
|
-
Requires-Dist: pre-commit; extra == "dev"
|
|
26
|
-
Dynamic: description
|
|
27
|
-
Dynamic: description-content-type
|
|
28
|
-
Dynamic: home-page
|
|
29
|
-
Dynamic: provides-extra
|
|
30
|
-
Dynamic: requires-dist
|
|
31
|
-
Dynamic: requires-python
|
|
32
|
-
Dynamic: summary
|
|
33
|
-
|
|
34
|
-
<h1 align="center">
|
|
35
|
-
<br>
|
|
36
|
-
<a href="https://vantage6.ai"><img src="https://github.com/IKNL/guidelines/blob/master/resources/logos/vantage6.png?raw=true" alt="vantage6" width="350"></a>
|
|
37
|
-
</h1>
|
|
38
|
-
|
|
39
|
-
<h3 align=center> A Privacy Enhancing Technology (PET) Operations platform</h3>
|
|
40
|
-
<h3 align="center">
|
|
41
|
-
|
|
42
|
-
<!-- Badges go here-->
|
|
43
|
-
|
|
44
|
-
[](https://github.com/vantage6/vantage6/actions/workflows/release.yml)
|
|
45
|
-
[](https://badge.fury.io/py/vantage6)
|
|
46
|
-
[](https://github.com/vantage6/vantage6/actions/workflows/unit_tests.yml)
|
|
47
|
-
[](https://coveralls.io/github/vantage6/vantage6?branch=main)
|
|
48
|
-
[](https://app.codacy.com/gh/vantage6/vantage6/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
|
49
|
-
[](https://zenodo.org/badge/latestdoi/492818831)
|
|
50
|
-
[](https://discord.gg/yAyFf6Y)
|
|
51
|
-
[](https://research-software-directory.org/software/vantage6)
|
|
52
|
-
|
|
53
|
-
</h3>
|
|
54
|
-
|
|
55
|
-
<p align="center">
|
|
56
|
-
<a href="#books-quickstart">Quickstart</a> •
|
|
57
|
-
<a href="#project-structure">Project structure</a> •
|
|
58
|
-
<a href="#gift_heart-join-the-community">Join the community</a> •
|
|
59
|
-
<a href="#scroll-license">License</a> •
|
|
60
|
-
<a href="#black_nib-code-of-conduct">Code of conduct</a> •
|
|
61
|
-
<a href="#black_nib-references">References</a>
|
|
62
|
-
</p>
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
This repository is contains all the **vantage6** infrastructure source code. The **vantage6** technology enables to manage and deploy privacy enhancing technologies like Federated Learning (FL) and Multi-Party Computation (MPC). Please visit our [website](https://vantage6.ai) to learn more!
|
|
67
|
-
|
|
68
|
-
You can find more (user) documentation at [readthedocs](https://docs.vantage6.ai). If you have any questions, suggestions or just want to chat about federated learning: join our [Discord)](https://discord.gg/yAyFf6Y) channel.
|
|
69
|
-
|
|
70
|
-
## Infrastructure overview
|
|
71
|
-
|
|
72
|
-

|
|
73
|
-
|
|
74
|
-
_A High level overview of the vantage6 infrastructure. Vantage6 has both a
|
|
75
|
-
client-server and peer-to-peer architecture. The client is used by the researcher to
|
|
76
|
-
create (PET) computation requests. It is also used to manage users, organizations and
|
|
77
|
-
collaborations. The server contains users, organizations, collaborations, tasks and
|
|
78
|
-
their results. It provides a central access point for both the clients and nodes. The
|
|
79
|
-
nodes have access to privacy sensitive data and handle computation requests retrieved
|
|
80
|
-
from the server. Computation request are executed as separate containers on the node.
|
|
81
|
-
These containers are connected to containers at other nodes by a internal network._
|
|
82
|
-
|
|
83
|
-
## :books: Quickstart
|
|
84
|
-
|
|
85
|
-
### Requirements
|
|
86
|
-
|
|
87
|
-
The **vantage6** infrastructure is delivered in Docker images. To run these images, you
|
|
88
|
-
need to have [Docker](https://docs.docker.com/get-docker/) installed. To install the
|
|
89
|
-
latest version of the vantage6 CLI, you need to have
|
|
90
|
-
[Python](https://www.python.org/downloads/), we recommend using an environment manager
|
|
91
|
-
like [mini-conda](https://docs.conda.io/en/latest/miniconda.html).
|
|
92
|
-
|
|
93
|
-
Install the latest version of the vantage6 CLI by using:
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
pip install vantage6
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
This install the `v6` commands, which allows you to manage your nodes and servers. To view all available options, run:
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
v6 --help
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
For example you can create a local test setup by using:
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
v6 dev create-demo-network
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
This creates a local network with a server and two nodes. You can start the network by running:
|
|
112
|
-
|
|
113
|
-
```bash
|
|
114
|
-
v6 dev start-demo-network
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
This will start the server and nodes in the background. You can view the logs by running:
|
|
118
|
-
|
|
119
|
-
```bash
|
|
120
|
-
# View node logs
|
|
121
|
-
v6 node attach
|
|
122
|
-
|
|
123
|
-
# View server logs
|
|
124
|
-
v6 server attach
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
From here you can use the [vantage6-client](https://pypi.org/project/vantage6-client)
|
|
128
|
-
to interact with the server. The demo network has a pre-configured organization with
|
|
129
|
-
the following credentials:
|
|
130
|
-
|
|
131
|
-
- Username: `dev_admin`
|
|
132
|
-
- Password: `password`
|
|
133
|
-
|
|
134
|
-
For example, you can create a new organization by running:
|
|
135
|
-
|
|
136
|
-
```python
|
|
137
|
-
from vantage6.client import Client
|
|
138
|
-
|
|
139
|
-
client = Client(
|
|
140
|
-
server_url='http://127.0.0.1:7601/api',
|
|
141
|
-
auth_url='http://127.0.0.1:8080',
|
|
142
|
-
log_level='debug'
|
|
143
|
-
)
|
|
144
|
-
client.authenticate()
|
|
145
|
-
client.setup_encryption(None)
|
|
146
|
-
|
|
147
|
-
client.organization.create(
|
|
148
|
-
name='My organization',
|
|
149
|
-
address1='My address',
|
|
150
|
-
address2='My address',
|
|
151
|
-
zipcode='1234AB',
|
|
152
|
-
country='The Netherlands',
|
|
153
|
-
domain='my-organization.com'
|
|
154
|
-
)
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
You can find more (user) documentation at [readthedocs](https://docs.vantage6.ai)
|
|
158
|
-
|
|
159
|
-
## Project structure
|
|
160
|
-
|
|
161
|
-
### PYPI packages
|
|
162
|
-
|
|
163
|
-
This repository is home to 6 PyPi packages:
|
|
164
|
-
|
|
165
|
-
- [vantage6](https://pypi.org/project/vantage6) -> _CLI for managing node and server instances_
|
|
166
|
-
- [vantage6-client](https://pypi.org/project/vantage6-client) -> _Python client for interacting with the vantage6-server_
|
|
167
|
-
- [vantage6-algorithm-tools](https://pypi.org/project/vantage6-algorithm-tools) -> _Python tools to facilitate algorithm development_
|
|
168
|
-
- [vantage6-node](https://pypi.org/project/vantage6-node) -> _Node application package_
|
|
169
|
-
- [vantage6-server](https://pypi.org/project/vantage6-server) -> _Server application package_
|
|
170
|
-
- [vantage6-algorithm-store](https://pypi.org/project/vantage6-algorithm-store) -> _Algorithm store application package_
|
|
171
|
-
- [vantage6-common](https://pypi.org/project/vantage6-common) -> _Package with common vantage6 functions_
|
|
172
|
-
- [vantage6-backend-common](https://pypi.org/project/vantage6-backend-common) -> _Package with functions common to central server and algorithm store_
|
|
173
|
-
|
|
174
|
-
**Note that when using vantage6 you do not install the _server_ and _node_ packages. These are delivered to you in Docker images.**
|
|
175
|
-
|
|
176
|
-
This repository also hosts the code for the vantage6 user interface (UI). The UI
|
|
177
|
-
is an Angular web application that can be used to interact with the vantage6 server
|
|
178
|
-
easily.
|
|
179
|
-
|
|
180
|
-
### Docker images
|
|
181
|
-
|
|
182
|
-
The vantage6 infrastructure is delivered in Docker images. All Docker images are stored
|
|
183
|
-
in our private [Harbor](https://goharbor.io/) registry. The most important images are:
|
|
184
|
-
|
|
185
|
-
- `harbor2.vantage6.ai/infrastructure/node:VERSION` -> _Node application Docker image_
|
|
186
|
-
- `harbor2.vantage6.ai/infrastructure/server:VERSION` -> _Server application Docker image_
|
|
187
|
-
- `harbor2.vantage6.ai/infrastructure/ui:VERSION` -> _User interface Docker image_
|
|
188
|
-
- `harbor2.vantage6.ai/infrastructure/algorithm-store:VERSION` -> _Algorithm store Docker image_
|
|
189
|
-
|
|
190
|
-
with `VERSION` being the full semantic version of the vantage6 infrastructure, e.g.
|
|
191
|
-
`4.0.0` or `4.1.0rc0`.
|
|
192
|
-
|
|
193
|
-
Several other images are used to support the infrastructure:
|
|
194
|
-
|
|
195
|
-
- `harbor2.vantage6.ai/infrastructure/infrastructure-base:VERSION` -> _Base image for the infrastructure_
|
|
196
|
-
- `harbor2.vantage6.ai/infrastructure/alpine` -> _Alpine image used for vpn traffic forwarding_
|
|
197
|
-
|
|
198
|
-
And finally there are some images released for algorithm development:
|
|
199
|
-
|
|
200
|
-
- `harbor2.vantage6.ai/infrastructure/algorithm-base:MAJOR.MINOR` -> _Base image for algorithm development_
|
|
201
|
-
- `harbor2.vantage6.ai/infrastructure/algorithm-ohdsi-base:MAJOR.MINOR` -> _Extended algorithm base image for OHDSI algorithm development_
|
|
202
|
-
|
|
203
|
-
## :gift_heart: Join the community!
|
|
204
|
-
|
|
205
|
-
We hope to continue developing, improving, and supporting **vantage6** with the help of
|
|
206
|
-
the federated learning community. If you are interested in contributing, first of all,
|
|
207
|
-
thank you! Second, please take a look at our
|
|
208
|
-
[contributing guidelines](https://docs.vantage6.ai/en/main/devops/contribute.html)
|
|
209
|
-
and our [code of conduct](CODE_OF_CONDUCT.md).
|
|
210
|
-
|
|
211
|
-
<a href="https://github.com/vantage6/vantage6/graphs/contributors">
|
|
212
|
-
<img src="https://contrib.rocks/image?repo=vantage6/vantage6" />
|
|
213
|
-
</a>
|
|
214
|
-
|
|
215
|
-
## :scroll: License
|
|
216
|
-
|
|
217
|
-
This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
|
|
218
|
-
|
|
219
|
-
## :black_nib: Code of Conduct
|
|
220
|
-
|
|
221
|
-
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). **By participating in any way in this project you agree to abide by its terms.**
|
|
222
|
-
|
|
223
|
-
## :black_nib: References
|
|
224
|
-
|
|
225
|
-
If you are using **vantage6**, please cite this repository as well as the accompanying papers as follows:
|
|
226
|
-
|
|
227
|
-
> - F. Martin, M. Sieswerda, H. Alradhi, et al. vantage6. Available at https://doi.org/10.5281/zenodo.7221216. Accessed on MONTH, 20XX.
|
|
228
|
-
> - A. Moncada-Torres, F. Martin, M. Sieswerda, J. van Soest, G. Gelijnse. VANTAGE6: an open source priVAcy preserviNg federaTed leArninG infrastructurE for Secure Insight eXchange. AMIA Annual Symposium Proceedings, 2020, p. 870-877. [[BibTeX](https://arturomoncadatorres.com/bibtex/moncada-torres2020vantage6.txt), [PDF](https://vantage6.ai/vantage6/)]
|
|
229
|
-
> - D. Smits\*, B. van Beusekom\*, F. Martin, L. Veen, G. Geleijnse, A. Moncada-Torres, An Improved Infrastructure for Privacy-Preserving Analysis of Patient Data, Proceedings of the International Conference of Informatics, Management, and Technology in Healthcare (ICIMTH), vol. 25, 2022, p. 144-147. [[BibTeX](https://arturomoncadatorres.com/bibtex/smits2022improved.txt), [PDF](https://ebooks.iospress.nl/volumearticle/60190)]
|
|
230
|
-
|
|
231
|
-
---
|
|
232
|
-
|
|
233
|
-
<p align="center">
|
|
234
|
-
<a href="https://vantage6.ai">vantage6.ai</a> •
|
|
235
|
-
<a href="https://discord.gg/yAyFf6Y">Discord</a> •
|
|
236
|
-
<a href="https://vantage6.discourse.group/">Discourse</a> •
|
|
237
|
-
<a href="https://docs.vantage6.ai">User documentation</a>
|
|
238
|
-
</p>
|