vantage6 4.5.2__tar.gz → 4.13.3rc2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of vantage6 might be problematic. Click here for more details.

Files changed (95) hide show
  1. {vantage6-4.5.2 → vantage6-4.13.3rc2}/PKG-INFO +70 -15
  2. {vantage6-4.5.2 → vantage6-4.13.3rc2}/setup.py +13 -8
  3. vantage6-4.13.3rc2/tests_cli/test_client_script.py +23 -0
  4. {vantage6-4.5.2 → vantage6-4.13.3rc2}/tests_cli/test_node_cli.py +40 -7
  5. {vantage6-4.5.2 → vantage6-4.13.3rc2}/tests_cli/test_server_cli.py +4 -4
  6. {vantage6-4.5.2 → vantage6-4.13.3rc2}/tests_cli/test_wizard.py +16 -7
  7. vantage6-4.13.3rc2/vantage6/cli/__build__ +1 -0
  8. vantage6-4.13.3rc2/vantage6/cli/__init__.py +3 -0
  9. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/_version.py +1 -1
  10. vantage6-4.13.3rc2/vantage6/cli/algorithm/create.py +193 -0
  11. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/algorithm/update.py +5 -1
  12. vantage6-4.13.3rc2/vantage6/cli/algostore/attach.py +49 -0
  13. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/algostore/list.py +1 -1
  14. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/algostore/new.py +9 -3
  15. vantage6-4.13.3rc2/vantage6/cli/algostore/remove.py +52 -0
  16. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/algostore/start.py +11 -7
  17. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/algostore/stop.py +9 -9
  18. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/cli.py +6 -0
  19. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/common/start.py +92 -2
  20. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/common/utils.py +37 -7
  21. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/configuration_manager.py +2 -0
  22. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/configuration_wizard.py +185 -102
  23. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/context/algorithm_store.py +1 -1
  24. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/context/node.py +9 -5
  25. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/context/server.py +26 -0
  26. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/dev/create.py +282 -60
  27. vantage6-4.13.3rc2/vantage6/cli/dev/data/km_dataset.csv +2401 -0
  28. vantage6-4.13.3rc2/vantage6/cli/dev/data/olympic_athletes_2016.csv +2425 -0
  29. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/dev/remove.py +54 -6
  30. vantage6-4.13.3rc2/vantage6/cli/dev/start.py +123 -0
  31. vantage6-4.13.3rc2/vantage6/cli/dev/stop.py +47 -0
  32. vantage6-4.13.3rc2/vantage6/cli/dev/utils.py +24 -0
  33. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/globals.py +15 -5
  34. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/node/attach.py +7 -3
  35. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/node/clean.py +9 -3
  36. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/node/common/__init__.py +27 -5
  37. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/node/create_private_key.py +10 -2
  38. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/node/new.py +7 -3
  39. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/node/remove.py +4 -5
  40. vantage6-4.13.3rc2/vantage6/cli/node/restart.py +128 -0
  41. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/node/set_api_key.py +7 -3
  42. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/node/start.py +21 -9
  43. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/node/stop.py +7 -3
  44. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/node/version.py +7 -3
  45. vantage6-4.13.3rc2/vantage6/cli/prometheus/monitoring_manager.py +146 -0
  46. vantage6-4.13.3rc2/vantage6/cli/prometheus/prometheus.yml +5 -0
  47. vantage6-4.13.3rc2/vantage6/cli/rabbitmq/__init__.py +0 -0
  48. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/rabbitmq/queue_manager.py +2 -2
  49. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/server/attach.py +9 -5
  50. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/server/import_.py +6 -3
  51. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/server/list.py +1 -1
  52. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/server/new.py +7 -3
  53. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/server/remove.py +14 -4
  54. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/server/shell.py +1 -1
  55. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/server/start.py +72 -11
  56. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/server/stop.py +17 -6
  57. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/server/version.py +2 -2
  58. vantage6-4.13.3rc2/vantage6/cli/template/algo_store_config.j2 +23 -0
  59. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/template/node_config.j2 +15 -2
  60. vantage6-4.13.3rc2/vantage6/cli/template/server_config.j2 +35 -0
  61. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/template/server_import_config.j2 +3 -4
  62. vantage6-4.13.3rc2/vantage6/cli/test/algo_test_scripts/algo_test_arguments.py +33 -0
  63. vantage6-4.13.3rc2/vantage6/cli/test/algo_test_scripts/algo_test_script.py +88 -0
  64. vantage6-4.13.3rc2/vantage6/cli/test/client_script.py +150 -0
  65. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/test/feature_tester.py +14 -4
  66. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/test/integration_test.py +7 -7
  67. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/utils.py +5 -1
  68. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6.egg-info/PKG-INFO +70 -15
  69. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6.egg-info/SOURCES.txt +12 -0
  70. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6.egg-info/requires.txt +6 -5
  71. vantage6-4.5.2/vantage6/cli/__build__ +0 -1
  72. vantage6-4.5.2/vantage6/cli/__init__.py +0 -3
  73. vantage6-4.5.2/vantage6/cli/algorithm/create.py +0 -51
  74. vantage6-4.5.2/vantage6/cli/algostore/attach.py +0 -32
  75. vantage6-4.5.2/vantage6/cli/dev/start.py +0 -52
  76. vantage6-4.5.2/vantage6/cli/dev/stop.py +0 -32
  77. vantage6-4.5.2/vantage6/cli/rabbitmq/__init__.py +0 -28
  78. vantage6-4.5.2/vantage6/cli/template/server_config.j2 +0 -16
  79. {vantage6-4.5.2 → vantage6-4.13.3rc2}/setup.cfg +0 -0
  80. {vantage6-4.5.2 → vantage6-4.13.3rc2}/tests_cli/__init__.py +0 -0
  81. {vantage6-4.5.2 → vantage6-4.13.3rc2}/tests_cli/test_example.py +0 -0
  82. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/algostore/files.py +0 -0
  83. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/common/decorator.py +0 -0
  84. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/context/__init__.py +0 -0
  85. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/context/base_server.py +0 -0
  86. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/node/files.py +0 -0
  87. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/node/list.py +0 -0
  88. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/rabbitmq/definitions.py +0 -0
  89. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/rabbitmq/rabbitmq.config +0 -0
  90. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/server/common/__init__.py +0 -0
  91. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/server/files.py +0 -0
  92. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6/cli/test/common/diagnostic_runner.py +0 -0
  93. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6.egg-info/dependency_links.txt +0 -0
  94. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6.egg-info/entry_points.txt +0 -0
  95. {vantage6-4.5.2 → vantage6-4.13.3rc2}/vantage6.egg-info/top_level.txt +0 -0
@@ -1,11 +1,34 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: vantage6
3
- Version: 4.5.2
3
+ Version: 4.13.3rc2
4
4
  Summary: vantage6 command line interface
5
5
  Home-page: https://github.com/vantage6/vantage6
6
6
  Requires-Python: >=3.10
7
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.9.1
11
+ Requires-Dist: docker==7.1.0
12
+ Requires-Dist: ipython==8.10.0
13
+ Requires-Dist: jinja2==3.1.6
14
+ Requires-Dist: pandas>=1.5.3
15
+ Requires-Dist: questionary==2.1.1
16
+ Requires-Dist: rich==13.5.2
17
+ Requires-Dist: schema==0.7.5
18
+ Requires-Dist: SQLAlchemy==1.4.46
19
+ Requires-Dist: vantage6-common==4.13.3rc2
20
+ Requires-Dist: vantage6-client==4.13.3rc2
8
21
  Provides-Extra: dev
22
+ Requires-Dist: coverage==6.4.4; extra == "dev"
23
+ Requires-Dist: black; extra == "dev"
24
+ Requires-Dist: pre-commit; extra == "dev"
25
+ Dynamic: description
26
+ Dynamic: description-content-type
27
+ Dynamic: home-page
28
+ Dynamic: provides-extra
29
+ Dynamic: requires-dist
30
+ Dynamic: requires-python
31
+ Dynamic: summary
9
32
 
10
33
  <h1 align="center">
11
34
  <br>
@@ -23,6 +46,9 @@ Provides-Extra: dev
23
46
  [![Coverage Status](https://coveralls.io/repos/github/vantage6/vantage6/badge.svg?branch=main)](https://coveralls.io/github/vantage6/vantage6?branch=main)
24
47
  [![Codacy Badge](https://app.codacy.com/project/badge/Grade/2e60ac3b3f284620805f7399cba317be)](https://app.codacy.com/gh/vantage6/vantage6/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
25
48
  [![DOI](https://zenodo.org/badge/492818831.svg)](https://zenodo.org/badge/latestdoi/492818831)
49
+ [![Discord](https://img.shields.io/discord/643526403207331841)](https://discord.gg/yAyFf6Y)
50
+ [![Research software directory](https://img.shields.io/badge/rsd-vantage6-deepskyblue)](https://research-software-directory.org/software/vantage6)
51
+
26
52
 
27
53
  </h3>
28
54
 
@@ -30,26 +56,39 @@ Provides-Extra: dev
30
56
  <a href="#books-quickstart">Quickstart</a> •
31
57
  <a href="#project-structure">Project structure</a> •
32
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> •
33
61
  <a href="#black_nib-references">References</a>
34
62
  </p>
35
63
 
36
64
  ---
37
65
 
38
- 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 (vantage6.ai)](https://vantage6.ai) to learn more!
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!
39
67
 
40
- You can find more (user) documentation at [readthedocs (docs.vantage6.ai)](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)](https://discord.gg/yAyFf6Y) channel.
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.
41
69
 
42
70
  ## Infrastructure overview
43
71
 
44
72
  ![Vantage6 architecture overview](docs/images/overview-infrastructure.png)
45
73
 
46
- _A High level overview of the vantage6 infrastructure. Vantage6 has both a client-server and peer-to-peer architecture. The client is used by the researcher to create (PET) computation requests. It is also used to manage users, organizations and collaborations. The server contains users, organizations, collaborations, tasks and their results. It provides a central access point for both the clients and nodes. The nodes have access to privacy sensitive data and handle computation requests retrieved from the server. Computation request are executed as separate containers on the node. These containers are connected to containers at other nodes by a VPN network._
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 VPN network._
47
82
 
48
83
  ## :books: Quickstart
49
84
 
50
85
  ### Requirements
51
86
 
52
- The **vantage6** infrastructure is delivered in Docker images. To run these images, you need to have [Docker](https://docs.docker.com/get-docker/) installed. To install the latest version of the vantage6 CLI, you need to have [Python](https://www.python.org/downloads/), we recommend using an environment manager like [mini-conda](https://docs.conda.io/en/latest/miniconda.html).
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).
53
92
 
54
93
  Install the latest version of the vantage6 CLI by using:
55
94
 
@@ -85,9 +124,11 @@ v6 node attach
85
124
  v6 server attach
86
125
  ```
87
126
 
88
- From here you can use the [vantage6-client](https://pypi.org/project/vantage6-client) to interact with the server. The demo network has a pre-configured organization with the following credentials:
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:
89
130
 
90
- - Username: `org_1-admin`
131
+ - Username: `dev_admin`
91
132
  - Password: `password`
92
133
 
93
134
  For example, you can create a new organization by running:
@@ -95,8 +136,8 @@ For example, you can create a new organization by running:
95
136
  ```python
96
137
  from vantage6.client import Client
97
138
 
98
- client = Client('http://127.0.0.1', 5000, '/api', log_level='debug')
99
- client.authenticate('org_1-admin', 'password')
139
+ client = Client('http://127.0.0.1', 7601, '/api', log_level='debug')
140
+ client.authenticate('dev_admin', 'password')
100
141
  client.setup_encryption(None)
101
142
 
102
143
  client.organization.create(
@@ -109,7 +150,7 @@ client.organization.create(
109
150
  )
110
151
  ```
111
152
 
112
- You can find more (user) documentation at [readthedocs (docs.vantage6.ai)](https://docs.vantage6.ai)
153
+ You can find more (user) documentation at [readthedocs](https://docs.vantage6.ai)
113
154
 
114
155
  ## Project structure
115
156
 
@@ -134,14 +175,16 @@ easily.
134
175
 
135
176
  ### Docker images
136
177
 
137
- The vantage6 infrastructure is delivered in Docker images. All Docker images are stored in our private [Harbor](https://goharbor.io/) registry. The most important images are:
178
+ The vantage6 infrastructure is delivered in Docker images. All Docker images are stored
179
+ in our private [Harbor](https://goharbor.io/) registry. The most important images are:
138
180
 
139
181
  - `harbor2.vantage6.ai/infrastructure/node:VERSION` -> _Node application Docker image_
140
182
  - `harbor2.vantage6.ai/infrastructure/server:VERSION` -> _Server application Docker image_
141
183
  - `harbor2.vantage6.ai/infrastructure/ui:VERSION` -> _User interface Docker image_
142
184
  - `harbor2.vantage6.ai/infrastructure/algorithm-store:VERSION` -> _Algorithm store Docker image_
143
185
 
144
- with `VERSION` being the full semantic version of the vantage6 infrastructure, e.g. `4.0.0` or `4.1.0rc0`.
186
+ with `VERSION` being the full semantic version of the vantage6 infrastructure, e.g.
187
+ `4.0.0` or `4.1.0rc0`.
145
188
 
146
189
  Several other images are used to support the infrastructure:
147
190
 
@@ -159,12 +202,24 @@ And finally there are some images released for algorithm development:
159
202
 
160
203
  ## :gift_heart: Join the community!
161
204
 
162
- We hope to continue developing, improving, and supporting **vantage6** with the help of the federated learning community. If you are interested in contributing, first of all, thank you! Second, please take a look at our [contributing guidelines](https://docs.vantage6.ai/en/main/devops/contribute.html)
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).
163
210
 
164
211
  <a href="https://github.com/vantage6/vantage6/graphs/contributors">
165
212
  <img src="https://contrib.rocks/image?repo=vantage6/vantage6" />
166
213
  </a>
167
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
+
168
223
  ## :black_nib: References
169
224
 
170
225
  If you are using **vantage6**, please cite this repository as well as the accompanying papers as follows:
@@ -179,5 +234,5 @@ If you are using **vantage6**, please cite this repository as well as the accomp
179
234
  <a href="https://vantage6.ai">vantage6.ai</a> •
180
235
  <a href="https://discord.gg/yAyFf6Y">Discord</a> •
181
236
  <a href="https://vantage6.discourse.group/">Discourse</a> •
182
- <a href="https://docs.vantage6.ai">User documentation</a>
237
+ <a href="https://docs.vantage6.ai">User documentation</a>
183
238
  </p>
@@ -1,11 +1,11 @@
1
1
  import codecs
2
2
  import os
3
-
4
- from os import path
5
3
  from codecs import open
6
- from setuptools import setup, find_namespace_packages
4
+ from os import path
7
5
  from pathlib import Path
8
6
 
7
+ from setuptools import find_namespace_packages, setup
8
+
9
9
  # get current directory
10
10
  here = Path(path.abspath(path.dirname(__file__)))
11
11
  parent_dir = here.parent.absolute()
@@ -35,16 +35,17 @@ setup(
35
35
  install_requires=[
36
36
  "click==8.1.3",
37
37
  "colorama==0.4.6",
38
- "copier==9.2.0",
38
+ "copier==9.9.1",
39
39
  "docker==7.1.0",
40
40
  "ipython==8.10.0",
41
- "jinja2==3.1.4",
42
- "questionary==1.10.0",
41
+ "jinja2==3.1.6",
42
+ "pandas>=1.5.3",
43
+ "questionary==2.1.1",
43
44
  "rich==13.5.2",
44
45
  "schema==0.7.5",
45
46
  "SQLAlchemy==1.4.46",
46
- f'vantage6-common == {version_ns["__version__"]}',
47
- f'vantage6-client == {version_ns["__version__"]}',
47
+ f"vantage6-common == {version_ns['__version__']}",
48
+ f"vantage6-client == {version_ns['__version__']}",
48
49
  ],
49
50
  extras_require={
50
51
  "dev": [
@@ -60,6 +61,10 @@ setup(
60
61
  "template/node_config.j2",
61
62
  "template/server_config.j2",
62
63
  "template/server_import_config.j2",
64
+ "template/algo_store_config.j2",
65
+ "dev/data/olympic_athletes_2016.csv",
66
+ "dev/data/km_dataset.csv",
67
+ "prometheus/prometheus.yml",
63
68
  ],
64
69
  },
65
70
  entry_points={
@@ -0,0 +1,23 @@
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
+ )
@@ -9,6 +9,7 @@ from io import BytesIO, StringIO
9
9
  from click.testing import CliRunner
10
10
  from docker.errors import APIError
11
11
 
12
+ from vantage6.common.globals import Ports
12
13
  from vantage6.cli.globals import APPNAME
13
14
  from vantage6.common import STRING_ENCODING
14
15
  from vantage6.cli.common.utils import print_log_worker
@@ -16,6 +17,7 @@ from vantage6.cli.node.list import cli_node_list
16
17
  from vantage6.cli.node.new import cli_node_new_configuration
17
18
  from vantage6.cli.node.files import cli_node_files
18
19
  from vantage6.cli.node.start import cli_node_start
20
+ from vantage6.cli.node.restart import cli_node_restart
19
21
  from vantage6.cli.node.stop import cli_node_stop
20
22
  from vantage6.cli.node.attach import cli_node_attach
21
23
  from vantage6.cli.node.create_private_key import cli_node_create_private_key
@@ -84,7 +86,7 @@ class NodeCLITest(unittest.TestCase):
84
86
  )
85
87
 
86
88
  @patch("vantage6.cli.node.new.configuration_wizard")
87
- @patch("vantage6.cli.node.new.check_config_writeable")
89
+ @patch("vantage6.cli.node.new.ensure_config_dir_writable")
88
90
  @patch("vantage6.cli.node.common.NodeContext")
89
91
  def test_new_config(self, context, permissions, wizard):
90
92
  """No error produced when creating new configuration."""
@@ -146,7 +148,7 @@ class NodeCLITest(unittest.TestCase):
146
148
  # check non-zero exit code
147
149
  self.assertEqual(result.exit_code, 1)
148
150
 
149
- @patch("vantage6.cli.node.new.check_config_writeable")
151
+ @patch("vantage6.cli.node.new.ensure_config_dir_writable")
150
152
  @patch("vantage6.cli.node.common.NodeContext")
151
153
  def test_new_write_permissions(self, context, permissions):
152
154
  """User needs write permissions."""
@@ -207,7 +209,7 @@ class NodeCLITest(unittest.TestCase):
207
209
  self.assertNotEqual(result.exit_code, 0)
208
210
 
209
211
  @patch("docker.DockerClient.volumes")
210
- @patch("vantage6.cli.node.start.pull_image")
212
+ @patch("vantage6.cli.node.start.pull_infra_image")
211
213
  @patch("vantage6.cli.common.decorator.get_context")
212
214
  @patch("docker.DockerClient.containers")
213
215
  @patch("vantage6.cli.node.start.check_docker_running", return_value=True)
@@ -242,19 +244,28 @@ class NodeCLITest(unittest.TestCase):
242
244
 
243
245
  runner = CliRunner()
244
246
 
247
+ # Should fail when starting node with non-existing database CSV file
245
248
  with runner.isolated_filesystem():
246
249
  result = runner.invoke(cli_node_start, ["--name", "some-name"])
250
+ self.assertEqual(result.exit_code, 1)
247
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"])
248
256
  self.assertEqual(result.exit_code, 0)
249
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
+
250
263
  @patch("docker.DockerClient.containers")
251
264
  @patch("vantage6.cli.node.stop.check_docker_running", return_value=True)
252
265
  @patch("vantage6.cli.node.stop.NodeContext")
253
266
  @patch("vantage6.cli.node.stop.delete_volume_if_exists")
254
267
  def test_stop(self, delete_volume, node_context, check_docker, containers):
255
- container1 = MagicMock()
256
- container1.name = f"{APPNAME}-iknl-user"
257
- containers.list.return_value = [container1]
268
+ self._setup_stop_test(containers)
258
269
 
259
270
  runner = CliRunner()
260
271
 
@@ -266,6 +277,24 @@ class NodeCLITest(unittest.TestCase):
266
277
 
267
278
  self.assertEqual(result.exit_code, 0)
268
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
+
269
298
  @patch("vantage6.cli.node.attach.time")
270
299
  @patch("vantage6.cli.node.attach.print_log_worker")
271
300
  @patch("docker.DockerClient.containers")
@@ -408,7 +437,11 @@ class NodeCLITest(unittest.TestCase):
408
437
  @patch("vantage6.cli.node.common.q")
409
438
  def test_client(self, q, client, error, debug, info):
410
439
  ctx = MagicMock(
411
- config={"server_url": "localhost", "port": 5000, "api_path": ""}
440
+ config={
441
+ "server_url": "localhost",
442
+ "port": Ports.DEV_SERVER.value,
443
+ "api_path": "",
444
+ }
412
445
  )
413
446
 
414
447
  # should not trigger an exception
@@ -18,7 +18,7 @@ class ServerCLITest(unittest.TestCase):
18
18
  @patch("vantage6.cli.server.start.NetworkManager")
19
19
  @patch("vantage6.cli.server.start.docker.types.Mount")
20
20
  @patch("os.makedirs")
21
- @patch("vantage6.cli.server.start.pull_image")
21
+ @patch("vantage6.cli.server.start.pull_infra_image")
22
22
  @patch("vantage6.cli.common.decorator.get_context")
23
23
  @patch("vantage6.cli.server.start.docker.from_env")
24
24
  @patch("vantage6.cli.common.start.check_docker_running", return_value=True)
@@ -112,7 +112,7 @@ class ServerCLITest(unittest.TestCase):
112
112
  self.assertEqual(result.exit_code, 0)
113
113
 
114
114
  @patch("vantage6.cli.server.new.configuration_wizard")
115
- @patch("vantage6.cli.server.new.check_config_writeable")
115
+ @patch("vantage6.cli.server.new.ensure_config_dir_writable")
116
116
  @patch("vantage6.cli.server.new.ServerContext")
117
117
  def test_new(self, context, permissions, wizard):
118
118
  """New configuration without errors."""
@@ -132,7 +132,7 @@ class ServerCLITest(unittest.TestCase):
132
132
  """Stop server without errors."""
133
133
 
134
134
  container1 = MagicMock()
135
- container1.name = f"{APPNAME}-iknl-system-{InstanceType.SERVER}"
135
+ container1.name = f"{APPNAME}-iknl-system-{InstanceType.SERVER.value}"
136
136
  containers.containers.list.return_value = [container1]
137
137
 
138
138
  runner = CliRunner()
@@ -146,7 +146,7 @@ class ServerCLITest(unittest.TestCase):
146
146
  def test_attach(self, containers, sleep):
147
147
  """Attach log to the console without errors."""
148
148
  container1 = MagicMock()
149
- container1.name = f"{APPNAME}-iknl-system-{InstanceType.SERVER}"
149
+ container1.name = f"{APPNAME}-iknl-system-{InstanceType.SERVER.value}"
150
150
  containers.list.return_value = [container1]
151
151
 
152
152
  sleep.side_effect = KeyboardInterrupt("Boom!")
@@ -9,7 +9,7 @@ from vantage6.cli.configuration_wizard import (
9
9
  configuration_wizard,
10
10
  select_configuration_questionaire,
11
11
  )
12
- from vantage6.common.globals import InstanceType
12
+ from vantage6.common.globals import InstanceType, NodePolicy
13
13
 
14
14
  module_path = "vantage6.cli.configuration_wizard"
15
15
 
@@ -33,14 +33,20 @@ class WizardTest(unittest.TestCase):
33
33
  """An error is printed when docker is not running"""
34
34
 
35
35
  with patch(f"{module_path}.q") as q:
36
- q.prompt.side_effect = self.prompts
37
- q.confirm.return_value.ask.side_effect = [
36
+ q.unsafe_prompt.side_effect = self.prompts
37
+ q.confirm.return_value.unsafe_ask.side_effect = [
38
38
  True, # add a database
39
39
  False, # don't enable two-factor authentication
40
40
  True, # add VPN server
41
41
  True, # add algorithm policies
42
+ True, # add single algorithms to allowed_algorithms
42
43
  "some-image", # algorithm image to whitelist
43
44
  False, # don't add another algorithm image
45
+ True, # add algorithm stores to allowed_algorithm_stores
46
+ "some-store", # algorithm store to whitelist
47
+ False, # don't add another algorithm store
48
+ False, # answer question on combining policies on store level and
49
+ # single algorithm level
44
50
  False, # don't abort if no server connection is made to pull
45
51
  # collaboration settings
46
52
  True, # Enable encryption
@@ -61,7 +67,10 @@ class WizardTest(unittest.TestCase):
61
67
  ]
62
68
  for key in keys:
63
69
  self.assertIn(key, config)
64
- nested_keys = [["policies", "allowed_algorithms"]]
70
+ nested_keys = [
71
+ ["policies", NodePolicy.ALLOWED_ALGORITHMS],
72
+ ["policies", NodePolicy.ALLOWED_ALGORITHM_STORES],
73
+ ]
65
74
  for nesting in nested_keys:
66
75
  current_config = config
67
76
  for key in nesting:
@@ -70,8 +79,8 @@ class WizardTest(unittest.TestCase):
70
79
 
71
80
  def test_server_wizard(self):
72
81
  with patch(f"{module_path}.q") as q:
73
- q.prompt.side_effect = self.prompts
74
- q.confirm.return_value.ask.side_effect = [
82
+ q.unsafe_prompt.side_effect = self.prompts
83
+ q.confirm.return_value.unsafe_ask.side_effect = [
75
84
  True,
76
85
  True,
77
86
  True,
@@ -125,7 +134,7 @@ class WizardTest(unittest.TestCase):
125
134
  available_configurations.return_value = [[config], []]
126
135
 
127
136
  with patch(f"{module_path}.q") as q:
128
- q.select.return_value.ask.return_value = "vtg6"
137
+ q.select.return_value.unsafe_ask.return_value = "vtg6"
129
138
  name = select_configuration_questionaire(InstanceType.NODE, True)
130
139
 
131
140
  self.assertEqual(name, "vtg6")
@@ -0,0 +1 @@
1
+ 2
@@ -0,0 +1,3 @@
1
+ """Command line interface for the vantage6 infrastructure."""
2
+
3
+ from ._version import version_info, __version__ # noqa: F401
@@ -7,7 +7,7 @@ with open(os.path.join(here, "__build__")) as fp:
7
7
  __build__ = json.load(fp)
8
8
 
9
9
  # Module version
10
- version_info = (4, 5, 2, "final", __build__, 0)
10
+ version_info = (4, 13, 3, "candidate", __build__, 0)
11
11
 
12
12
  # Module version stage suffix map
13
13
  _specifier_ = {"alpha": "a", "beta": "b", "candidate": "rc", "final": ""}
@@ -0,0 +1,193 @@
1
+ import os
2
+ import re
3
+ import subprocess
4
+ from pathlib import Path
5
+
6
+ import click
7
+ import questionary as q
8
+ from copier import run_copy
9
+
10
+ from vantage6.common import error, warning
11
+
12
+ from vantage6.cli import __version__
13
+ from vantage6.cli.globals import ALGORITHM_TEMPLATE_REPO
14
+ from vantage6.cli.utils import info
15
+
16
+
17
+ @click.command()
18
+ @click.option(
19
+ "-n", "--name", default=None, type=str, help="Name for your new algorithm"
20
+ )
21
+ @click.option(
22
+ "-d",
23
+ "--dir",
24
+ "directory",
25
+ default=None,
26
+ type=str,
27
+ help="Directory to put the algorithm into",
28
+ )
29
+ @click.option(
30
+ "--major-version",
31
+ default=None,
32
+ type=int,
33
+ help="Major version of the algorithm. By default, the current version is used.",
34
+ )
35
+ def cli_algorithm_create(name: str, directory: str, major_version: int | None) -> dict:
36
+ """Creates a personalized template for a new algorithm
37
+
38
+ By answering a number of questions, a template will be created that will
39
+ simplify the creation of a new algorithm. The goal is that the algorithm
40
+ developer only focuses on the algorithm code rather than fitting it to
41
+ the vantage6 infrastructure.
42
+
43
+ The created template will contain a Python package with a Dockerfile that
44
+ can be used to build an appropriate Docker image that can be used as a
45
+ vantage6 algorithm.
46
+ """
47
+ latest_tag_of_desired_major_version = None
48
+ if major_version is None:
49
+ major_version = int(__version__.split(".")[0])
50
+ latest_tag_of_desired_major_version = _get_latest_major_tag(major_version)
51
+
52
+ try:
53
+ name, directory = _get_user_input(name, directory)
54
+ except KeyboardInterrupt:
55
+ info("Aborted by user!")
56
+ return
57
+
58
+ # Create the template. The `unsafe` flag is used to allow running a Python script
59
+ # after creating the template that cleans up some things.
60
+ run_copy(
61
+ ALGORITHM_TEMPLATE_REPO,
62
+ directory,
63
+ data={"algorithm_name": name},
64
+ unsafe=True,
65
+ vcs_ref=latest_tag_of_desired_major_version,
66
+ )
67
+ info("Template created!")
68
+ info(f"You can find your new algorithm in: {directory}")
69
+
70
+
71
+ def _get_user_input(name: str, directory: str) -> None:
72
+ """Get user input for the algorithm creation
73
+
74
+ Parameters
75
+ ----------
76
+ name : str
77
+ Name for the new algorithm
78
+ directory : str
79
+ Directory to put the algorithm into
80
+ """
81
+ if not name:
82
+ name = q.text("Name of your new algorithm:").unsafe_ask()
83
+
84
+ if not directory:
85
+ default_dir = str(Path(os.getcwd()) / name)
86
+ directory = q.text(
87
+ "Directory to put the algorithm in:", default=default_dir
88
+ ).unsafe_ask()
89
+ return name, directory
90
+
91
+
92
+ def _get_latest_major_tag(major_version: int) -> str | None:
93
+ """Get the latest tag for the given major version"""
94
+ # get the tags from the algorithm template repository
95
+ try:
96
+ tags = _get_algo_template_tags(ALGORITHM_TEMPLATE_REPO)
97
+ except Exception as e:
98
+ error(f"Failed to fetch tags from {ALGORITHM_TEMPLATE_REPO}: {e}")
99
+ warning("Will use latest version instead.")
100
+ return None
101
+ # Filter tags for the given major version (e.g. 5.x.x)
102
+ tags_in_desired_major_version = [
103
+ tag
104
+ for tag in tags
105
+ if tag.startswith(f"{major_version}.")
106
+ and re.match(rf"^{major_version}\.\d+\.\d+", tag)
107
+ ]
108
+
109
+ # sort the tags in descending order
110
+ tags_in_desired_major_version.sort(key=lambda s: _gen_sort_key(s), reverse=True)
111
+ return _first_non_prerelease_tag(tags_in_desired_major_version)
112
+
113
+
114
+ def _first_non_prerelease_tag(tags: list[str]) -> str:
115
+ """Return the first non-prerelease tag from a list of tags"""
116
+ for tag in tags:
117
+ patch = tag.split(".")[2]
118
+ try:
119
+ int(patch)
120
+ return tag
121
+ except ValueError:
122
+ continue
123
+ # no non-prerelease tag found - return first in the list (sorted in descending
124
+ # order)
125
+ return tags[0] if tags else None
126
+
127
+
128
+ def _gen_sort_key(tag: str) -> list[int]:
129
+ """Generate a sort key for a tag"""
130
+ major = int(tag.split(".")[0])
131
+ minor = int(tag.split(".")[1])
132
+ # Note: patch is not cast to int for sorting, because it may contain
133
+ # alpha/beta/rc suffixes
134
+ # TODO this will go wrong in sorting 1.2.13a1 vs 1.2.3a1, but we don't care
135
+ # about that for now, as it is unlikely that we have 10+ patch releases
136
+ # for the algorithm template repository
137
+ patch = tag.split(".")[2]
138
+ return [major, minor, patch]
139
+
140
+
141
+ def _get_algo_template_tags(repo_url: str) -> list[str]:
142
+ """Get all tags from a git repository
143
+
144
+ Parameters
145
+ ----------
146
+ repo_url : str
147
+ Repository URL in format like "gh:owner/repo.git" or full git URL
148
+
149
+ Returns
150
+ -------
151
+ list[str]
152
+ List of tag names (without refs/tags/ prefix)
153
+ """
154
+ # Convert copier format (gh:owner/repo.git) to git URL
155
+ if repo_url.startswith("gh:"):
156
+ # Format: gh:owner/repo.git -> https://github.com/owner/repo.git
157
+ repo_path = repo_url[3:].rstrip(".git")
158
+ git_url = f"https://github.com/{repo_path}.git"
159
+ else:
160
+ git_url = repo_url
161
+
162
+ try:
163
+ # Use git ls-remote to fetch tags without cloning
164
+ result = subprocess.run(
165
+ ["git", "ls-remote", "--tags", git_url],
166
+ capture_output=True,
167
+ text=True,
168
+ check=True,
169
+ timeout=10,
170
+ )
171
+
172
+ # Parse output: lines look like "hash\trefs/tags/v1.0.0"
173
+ tags = []
174
+ for line in result.stdout.strip().split("\n"):
175
+ if line:
176
+ # Extract tag name from "refs/tags/tagname"
177
+ match = re.search(r"refs/tags/(.+)", line)
178
+ if match:
179
+ tag = match.group(1)
180
+ # Filter out ^{} suffix that git adds for annotated tags
181
+ if not tag.endswith("^{}"):
182
+ tags.append(tag)
183
+
184
+ return sorted(tags)
185
+ except subprocess.CalledProcessError as e:
186
+ info(f"Failed to fetch tags from {git_url}: {e}")
187
+ return []
188
+ except subprocess.TimeoutExpired:
189
+ info(f"Timeout while fetching tags from {git_url}")
190
+ return []
191
+ except FileNotFoundError:
192
+ info("git command not found. Please install git to fetch repository tags.")
193
+ return []