vantage6-node 4.15.2__tar.gz → 4.15.3__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.
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/PKG-INFO +4 -4
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/tests/docker/test_docker_manager.py +1 -3
- vantage6_node-4.15.3/tests/test_algorithm_policies.py +80 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/__init__.py +38 -21
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/_version.py +1 -1
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/docker_manager.py +1 -12
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/task_manager.py +2 -11
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/socket.py +1 -9
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6_node.egg-info/PKG-INFO +4 -4
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6_node.egg-info/SOURCES.txt +1 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6_node.egg-info/requires.txt +3 -3
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/setup.cfg +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/setup.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/tests/__init__.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/tests/docker/__init__.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/tests/test_ssh_tunnel.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/__build__ +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/cli/__init__.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/cli/node.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/context.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/docker_base.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/exceptions.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/squid.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/ssh_tunnel.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/utils.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/vpn_manager.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/globals.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/proxy_server.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/util/__init__.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/util/colorer.py +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6_node.egg-info/dependency_links.txt +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6_node.egg-info/entry_points.txt +0 -0
- {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6_node.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vantage6_node
|
|
3
|
-
Version: 4.15.
|
|
3
|
+
Version: 4.15.3
|
|
4
4
|
Summary: vantage6 node
|
|
5
5
|
Home-page: https://github.com/vantage6/vantage6
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -13,9 +13,9 @@ Requires-Dist: python-socketio==5.15.0
|
|
|
13
13
|
Requires-Dist: requests==2.33.1
|
|
14
14
|
Requires-Dist: psutil==5.9.0
|
|
15
15
|
Requires-Dist: nvidia-ml-py==12.535.133
|
|
16
|
-
Requires-Dist: vantage6==4.15.
|
|
17
|
-
Requires-Dist: vantage6-client==4.15.
|
|
18
|
-
Requires-Dist: vantage6-algorithm-tools==4.15.
|
|
16
|
+
Requires-Dist: vantage6==4.15.3
|
|
17
|
+
Requires-Dist: vantage6-client==4.15.3
|
|
18
|
+
Requires-Dist: vantage6-algorithm-tools==4.15.3
|
|
19
19
|
Provides-Extra: dev
|
|
20
20
|
Requires-Dist: coverage==6.4.4; extra == "dev"
|
|
21
21
|
Requires-Dist: python-coveralls==2.9.3; extra == "dev"
|
|
@@ -31,9 +31,7 @@ class TestIsDockerImageAllowed(unittest.TestCase):
|
|
|
31
31
|
|
|
32
32
|
def test_allowed_algorithms_exact_match(self):
|
|
33
33
|
"""Baseline regression check for the (unchanged) allowed_algorithms policy."""
|
|
34
|
-
self.manager._policies = {
|
|
35
|
-
NodePolicy.ALLOWED_ALGORITHMS: ["some/image:tag"]
|
|
36
|
-
}
|
|
34
|
+
self.manager._policies = {NodePolicy.ALLOWED_ALGORITHMS: ["some/image:tag"]}
|
|
37
35
|
|
|
38
36
|
self.assertTrue(
|
|
39
37
|
self.manager.is_docker_image_allowed("some/image:tag", self._task_info())
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import MagicMock, patch
|
|
3
|
+
|
|
4
|
+
from vantage6.common.globals import NodePolicy
|
|
5
|
+
|
|
6
|
+
from vantage6.node import Node
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestCheckAlgorithmPolicies(unittest.TestCase):
|
|
10
|
+
"""
|
|
11
|
+
Tests for Node.check_algorithm_policies().
|
|
12
|
+
|
|
13
|
+
Node.__init__ connects to the server and Docker, so these tests bypass it (via
|
|
14
|
+
__new__) and set up only the attributes the check reads: `config` and `log`.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def _node(self, config: dict) -> Node:
|
|
18
|
+
node = Node.__new__(Node)
|
|
19
|
+
node.config = config
|
|
20
|
+
node.log = MagicMock()
|
|
21
|
+
return node
|
|
22
|
+
|
|
23
|
+
def _assert_exits(self, config: dict):
|
|
24
|
+
node = self._node(config)
|
|
25
|
+
with (
|
|
26
|
+
patch("vantage6.node.sys.exit", side_effect=SystemExit(1)) as mock_exit,
|
|
27
|
+
self.assertRaises(SystemExit),
|
|
28
|
+
):
|
|
29
|
+
node.check_algorithm_policies()
|
|
30
|
+
mock_exit.assert_called_once_with(1)
|
|
31
|
+
node.log.error.assert_called_once()
|
|
32
|
+
|
|
33
|
+
def _assert_starts(self, config: dict) -> Node:
|
|
34
|
+
node = self._node(config)
|
|
35
|
+
with patch("vantage6.node.sys.exit") as mock_exit:
|
|
36
|
+
node.check_algorithm_policies()
|
|
37
|
+
mock_exit.assert_not_called()
|
|
38
|
+
return node
|
|
39
|
+
|
|
40
|
+
def test_production_without_policies_exits(self):
|
|
41
|
+
self._assert_exits({"production": True})
|
|
42
|
+
|
|
43
|
+
def test_production_is_default(self):
|
|
44
|
+
self._assert_exits({})
|
|
45
|
+
|
|
46
|
+
def test_production_with_empty_policies_exits(self):
|
|
47
|
+
self._assert_exits(
|
|
48
|
+
{
|
|
49
|
+
"policies": {
|
|
50
|
+
NodePolicy.ALLOWED_ALGORITHMS: [],
|
|
51
|
+
NodePolicy.ALLOWED_ALGORITHM_STORES: [],
|
|
52
|
+
NodePolicy.ALLOWED_USERS: [1],
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def test_production_with_allowed_algorithms_starts(self):
|
|
58
|
+
node = self._assert_starts(
|
|
59
|
+
{
|
|
60
|
+
"production": True,
|
|
61
|
+
"policies": {NodePolicy.ALLOWED_ALGORITHMS: ["^some/image$"]},
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
node.log.warning.assert_not_called()
|
|
65
|
+
|
|
66
|
+
def test_production_with_allowed_stores_starts(self):
|
|
67
|
+
node = self._assert_starts(
|
|
68
|
+
{
|
|
69
|
+
"production": True,
|
|
70
|
+
"policies": {
|
|
71
|
+
NodePolicy.ALLOWED_ALGORITHM_STORES: ["https://store.example"]
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
node.log.warning.assert_not_called()
|
|
76
|
+
|
|
77
|
+
def test_non_production_without_policies_warns(self):
|
|
78
|
+
node = self._assert_starts({"production": False})
|
|
79
|
+
node.log.warning.assert_called()
|
|
80
|
+
node.log.error.assert_not_called()
|
|
@@ -64,7 +64,6 @@ from vantage6.node.globals import (
|
|
|
64
64
|
)
|
|
65
65
|
from vantage6.common.client.node_client import NodeClient
|
|
66
66
|
from vantage6.node import proxy_server
|
|
67
|
-
from vantage6.node.util import get_parent_id
|
|
68
67
|
from vantage6.node.docker.docker_manager import DockerManager
|
|
69
68
|
from vantage6.node.docker.utils import login_to_registries
|
|
70
69
|
from vantage6.node.docker.vpn_manager import VPNManager
|
|
@@ -117,6 +116,9 @@ class Node:
|
|
|
117
116
|
|
|
118
117
|
self.config = self.ctx.config
|
|
119
118
|
self.debug: dict = self.config.get("debug", {})
|
|
119
|
+
|
|
120
|
+
self.check_algorithm_policies()
|
|
121
|
+
|
|
120
122
|
self.queue = queue.Queue()
|
|
121
123
|
self._using_encryption = None
|
|
122
124
|
|
|
@@ -224,6 +226,36 @@ class Node:
|
|
|
224
226
|
|
|
225
227
|
self.log.info("Init complete")
|
|
226
228
|
|
|
229
|
+
def check_algorithm_policies(self) -> None:
|
|
230
|
+
"""
|
|
231
|
+
Verify that the node restricts which algorithms are allowed to run.
|
|
232
|
+
|
|
233
|
+
When ``production`` is true (default), the node exits if neither the
|
|
234
|
+
``allowed_algorithms`` nor the ``allowed_algorithm_stores`` policy is set.
|
|
235
|
+
Otherwise, all algorithms are allowed and a warning is logged.
|
|
236
|
+
"""
|
|
237
|
+
policies = self.config.get("policies") or {}
|
|
238
|
+
if policies.get(NodePolicy.ALLOWED_ALGORITHMS) or policies.get(
|
|
239
|
+
NodePolicy.ALLOWED_ALGORITHM_STORES
|
|
240
|
+
):
|
|
241
|
+
return
|
|
242
|
+
|
|
243
|
+
if self.config.get("production", True):
|
|
244
|
+
self.log.error(
|
|
245
|
+
"No policies on allowed algorithms have been set for this node. Nodes "
|
|
246
|
+
"with production=true require that you set the '%s' and/or '%s' "
|
|
247
|
+
"policy in the node configuration.",
|
|
248
|
+
NodePolicy.ALLOWED_ALGORITHMS.value,
|
|
249
|
+
NodePolicy.ALLOWED_ALGORITHM_STORES.value,
|
|
250
|
+
)
|
|
251
|
+
sys.exit(1)
|
|
252
|
+
self.log.warning(
|
|
253
|
+
"No policies on allowed algorithms have been set for this node!"
|
|
254
|
+
)
|
|
255
|
+
self.log.warning(
|
|
256
|
+
"This means that all algorithms are allowed to run on this node."
|
|
257
|
+
)
|
|
258
|
+
|
|
227
259
|
def __metadata_worker(self) -> None:
|
|
228
260
|
"""
|
|
229
261
|
Periodically send system metadata to the server.
|
|
@@ -516,15 +548,7 @@ class Node:
|
|
|
516
548
|
# send socket event to alert everyone of task status change
|
|
517
549
|
self.socketIO.emit(
|
|
518
550
|
"algorithm_status_change",
|
|
519
|
-
data={
|
|
520
|
-
"node_id": self.client.whoami.id_,
|
|
521
|
-
"status": task_status,
|
|
522
|
-
"run_id": task_incl_run["id"],
|
|
523
|
-
"task_id": task["id"],
|
|
524
|
-
"collaboration_id": self.client.collaboration_id,
|
|
525
|
-
"organization_id": self.client.whoami.organization_id,
|
|
526
|
-
"parent_id": get_parent_id(task),
|
|
527
|
-
},
|
|
551
|
+
data={"run_id": task_incl_run["id"], "status": task_status},
|
|
528
552
|
namespace="/tasks",
|
|
529
553
|
)
|
|
530
554
|
|
|
@@ -578,15 +602,7 @@ class Node:
|
|
|
578
602
|
# notify socket channel of algorithm status change
|
|
579
603
|
self.socketIO.emit(
|
|
580
604
|
"algorithm_status_change",
|
|
581
|
-
data={
|
|
582
|
-
"node_id": self.client.whoami.id_,
|
|
583
|
-
"status": results.status,
|
|
584
|
-
"run_id": results.run_id,
|
|
585
|
-
"task_id": results.task_id,
|
|
586
|
-
"collaboration_id": self.client.collaboration_id,
|
|
587
|
-
"organization_id": self.client.whoami.organization_id,
|
|
588
|
-
"parent_id": results.parent_id,
|
|
589
|
-
},
|
|
605
|
+
data={"run_id": results.run_id, "status": results.status},
|
|
590
606
|
namespace="/tasks",
|
|
591
607
|
)
|
|
592
608
|
|
|
@@ -1095,8 +1111,9 @@ class Node:
|
|
|
1095
1111
|
while not self.socketIO.connected:
|
|
1096
1112
|
if i > TIME_LIMIT_INITIAL_CONNECTION_WEBSOCKET:
|
|
1097
1113
|
self.log.critical(
|
|
1098
|
-
"Could not connect to the websocket channels
|
|
1099
|
-
"slow connection
|
|
1114
|
+
"Could not connect to the websocket channels. Either the "
|
|
1115
|
+
"connection is slow, or the server refused the connection because "
|
|
1116
|
+
"the node could not be authenticated (check the server logs)."
|
|
1100
1117
|
)
|
|
1101
1118
|
exit(1)
|
|
1102
1119
|
self.log.debug("Waiting for socket connection...")
|
|
@@ -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, 15,
|
|
10
|
+
version_info = (4, 15, 3, "final", __build__, 0)
|
|
11
11
|
|
|
12
12
|
# Module version stage suffix map
|
|
13
13
|
_specifier_ = {"alpha": "a", "beta": "b", "candidate": "rc", "final": ""}
|
|
@@ -350,18 +350,7 @@ class DockerManager(DockerBaseManager):
|
|
|
350
350
|
dict
|
|
351
351
|
Dictionary with the policies
|
|
352
352
|
"""
|
|
353
|
-
|
|
354
|
-
if not policies or (
|
|
355
|
-
not policies.get(NodePolicy.ALLOWED_ALGORITHMS)
|
|
356
|
-
and not policies.get(NodePolicy.ALLOWED_ALGORITHM_STORES)
|
|
357
|
-
):
|
|
358
|
-
self.log.warning(
|
|
359
|
-
"No policies on allowed algorithms have been set for this node!"
|
|
360
|
-
)
|
|
361
|
-
self.log.warning(
|
|
362
|
-
"This means that all algorithms are allowed to run on this node."
|
|
363
|
-
)
|
|
364
|
-
return policies
|
|
353
|
+
return config.get("policies") or {}
|
|
365
354
|
|
|
366
355
|
def create_volume(self, volume_name: str) -> None:
|
|
367
356
|
"""
|
|
@@ -400,8 +400,6 @@ class DockerTaskManager(DockerBaseManager):
|
|
|
400
400
|
share_algorithm_logs=self.share_algorithm_logs,
|
|
401
401
|
socketIO=self.socket,
|
|
402
402
|
run_id=self.run_id,
|
|
403
|
-
task_id=self.task_id,
|
|
404
|
-
collaboration_id=self.collaboration_id,
|
|
405
403
|
)
|
|
406
404
|
|
|
407
405
|
except Exception as e:
|
|
@@ -935,9 +933,7 @@ class DockerTaskManager(DockerBaseManager):
|
|
|
935
933
|
encoded_environment_variables[key] = _encode(str(val))
|
|
936
934
|
return encoded_environment_variables
|
|
937
935
|
|
|
938
|
-
def _stream_logs(
|
|
939
|
-
self, share_algorithm_logs, socketIO, run_id, task_id, collaboration_id
|
|
940
|
-
):
|
|
936
|
+
def _stream_logs(self, share_algorithm_logs, socketIO, run_id):
|
|
941
937
|
"""
|
|
942
938
|
Stream logs from the running container to node logs and optionally to
|
|
943
939
|
the server
|
|
@@ -955,12 +951,7 @@ class DockerTaskManager(DockerBaseManager):
|
|
|
955
951
|
if share_algorithm_logs:
|
|
956
952
|
socketIO.emit(
|
|
957
953
|
"algorithm_log",
|
|
958
|
-
data={
|
|
959
|
-
"collaboration_id": collaboration_id,
|
|
960
|
-
"run_id": run_id,
|
|
961
|
-
"task_id": task_id,
|
|
962
|
-
"log": decoded_log,
|
|
963
|
-
},
|
|
954
|
+
data={"run_id": run_id, "log": decoded_log},
|
|
964
955
|
namespace="/tasks",
|
|
965
956
|
)
|
|
966
957
|
|
|
@@ -154,14 +154,6 @@ class NodeTaskNamespace(ClientNamespace):
|
|
|
154
154
|
for killed in killed_ids:
|
|
155
155
|
self.emit(
|
|
156
156
|
"algorithm_status_change",
|
|
157
|
-
{
|
|
158
|
-
"run_id": killed.run_id,
|
|
159
|
-
"task_id": killed.task_id,
|
|
160
|
-
"collaboration_id": self.node_worker_ref.client.collaboration_id,
|
|
161
|
-
"node_id": self.node_worker_ref.client.whoami.id_,
|
|
162
|
-
"status": TaskStatus.KILLED,
|
|
163
|
-
"organization_id": self.node_worker_ref.client.whoami.organization_id,
|
|
164
|
-
"parent_id": killed.parent_id,
|
|
165
|
-
},
|
|
157
|
+
{"run_id": killed.run_id, "status": TaskStatus.KILLED},
|
|
166
158
|
namespace="/tasks",
|
|
167
159
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vantage6_node
|
|
3
|
-
Version: 4.15.
|
|
3
|
+
Version: 4.15.3
|
|
4
4
|
Summary: vantage6 node
|
|
5
5
|
Home-page: https://github.com/vantage6/vantage6
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -13,9 +13,9 @@ Requires-Dist: python-socketio==5.15.0
|
|
|
13
13
|
Requires-Dist: requests==2.33.1
|
|
14
14
|
Requires-Dist: psutil==5.9.0
|
|
15
15
|
Requires-Dist: nvidia-ml-py==12.535.133
|
|
16
|
-
Requires-Dist: vantage6==4.15.
|
|
17
|
-
Requires-Dist: vantage6-client==4.15.
|
|
18
|
-
Requires-Dist: vantage6-algorithm-tools==4.15.
|
|
16
|
+
Requires-Dist: vantage6==4.15.3
|
|
17
|
+
Requires-Dist: vantage6-client==4.15.3
|
|
18
|
+
Requires-Dist: vantage6-algorithm-tools==4.15.3
|
|
19
19
|
Provides-Extra: dev
|
|
20
20
|
Requires-Dist: coverage==6.4.4; extra == "dev"
|
|
21
21
|
Requires-Dist: python-coveralls==2.9.3; extra == "dev"
|
|
@@ -6,9 +6,9 @@ python-socketio==5.15.0
|
|
|
6
6
|
requests==2.33.1
|
|
7
7
|
psutil==5.9.0
|
|
8
8
|
nvidia-ml-py==12.535.133
|
|
9
|
-
vantage6==4.15.
|
|
10
|
-
vantage6-client==4.15.
|
|
11
|
-
vantage6-algorithm-tools==4.15.
|
|
9
|
+
vantage6==4.15.3
|
|
10
|
+
vantage6-client==4.15.3
|
|
11
|
+
vantage6-algorithm-tools==4.15.3
|
|
12
12
|
|
|
13
13
|
[dev]
|
|
14
14
|
coverage==6.4.4
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|