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.
Files changed (33) hide show
  1. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/PKG-INFO +4 -4
  2. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/tests/docker/test_docker_manager.py +1 -3
  3. vantage6_node-4.15.3/tests/test_algorithm_policies.py +80 -0
  4. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/__init__.py +38 -21
  5. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/_version.py +1 -1
  6. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/docker_manager.py +1 -12
  7. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/task_manager.py +2 -11
  8. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/socket.py +1 -9
  9. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6_node.egg-info/PKG-INFO +4 -4
  10. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6_node.egg-info/SOURCES.txt +1 -0
  11. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6_node.egg-info/requires.txt +3 -3
  12. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/setup.cfg +0 -0
  13. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/setup.py +0 -0
  14. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/tests/__init__.py +0 -0
  15. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/tests/docker/__init__.py +0 -0
  16. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/tests/test_ssh_tunnel.py +0 -0
  17. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/__build__ +0 -0
  18. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/cli/__init__.py +0 -0
  19. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/cli/node.py +0 -0
  20. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/context.py +0 -0
  21. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/docker_base.py +0 -0
  22. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/exceptions.py +0 -0
  23. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/squid.py +0 -0
  24. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/ssh_tunnel.py +0 -0
  25. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/utils.py +0 -0
  26. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/docker/vpn_manager.py +0 -0
  27. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/globals.py +0 -0
  28. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/proxy_server.py +0 -0
  29. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/util/__init__.py +0 -0
  30. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6/node/util/colorer.py +0 -0
  31. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6_node.egg-info/dependency_links.txt +0 -0
  32. {vantage6_node-4.15.2 → vantage6_node-4.15.3}/vantage6_node.egg-info/entry_points.txt +0 -0
  33. {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.2
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.2
17
- Requires-Dist: vantage6-client==4.15.2
18
- Requires-Dist: vantage6-algorithm-tools==4.15.2
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, do you have a "
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, 2, "final", __build__, 0)
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
- policies = config.get("policies", {})
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.2
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.2
17
- Requires-Dist: vantage6-client==4.15.2
18
- Requires-Dist: vantage6-algorithm-tools==4.15.2
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"
@@ -1,5 +1,6 @@
1
1
  setup.py
2
2
  tests/__init__.py
3
+ tests/test_algorithm_policies.py
3
4
  tests/test_ssh_tunnel.py
4
5
  tests/docker/__init__.py
5
6
  tests/docker/test_docker_manager.py
@@ -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.2
10
- vantage6-client==4.15.2
11
- vantage6-algorithm-tools==4.15.2
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