manta-node 0.5b0.dev88__py3-none-any.whl → 0.5b0.dev116__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.
- manta_node/infrastructure/mqtt/command_handler.py +59 -41
- manta_node/task_manager.py +59 -10
- {manta_node-0.5b0.dev88.dist-info → manta_node-0.5b0.dev116.dist-info}/METADATA +1 -1
- {manta_node-0.5b0.dev88.dist-info → manta_node-0.5b0.dev116.dist-info}/RECORD +8 -8
- {manta_node-0.5b0.dev88.dist-info → manta_node-0.5b0.dev116.dist-info}/WHEEL +0 -0
- {manta_node-0.5b0.dev88.dist-info → manta_node-0.5b0.dev116.dist-info}/entry_points.txt +0 -0
- {manta_node-0.5b0.dev88.dist-info → manta_node-0.5b0.dev116.dist-info}/licenses/LICENSE +0 -0
- {manta_node-0.5b0.dev88.dist-info → manta_node-0.5b0.dev116.dist-info}/top_level.txt +0 -0
|
@@ -10,7 +10,7 @@ import aiomqtt
|
|
|
10
10
|
from manta_common.base_mqtt import MqttBase
|
|
11
11
|
from manta_common.build.common.mqtt import MqMetricsSnapshot, MqTaskUpdate
|
|
12
12
|
from manta_common.build.common.system import Logs
|
|
13
|
-
from manta_common.build.common.tasks import TaskStatus, TaskUpdate
|
|
13
|
+
from manta_common.build.common.tasks import MqttTask, TaskStatus, TaskUpdate
|
|
14
14
|
from manta_common.const import CHUNK_SIZE
|
|
15
15
|
from manta_common.conversions import ID
|
|
16
16
|
from manta_common.errors import (
|
|
@@ -121,52 +121,41 @@ class CommandHandler(TaskManager, Collector, MqttBase):
|
|
|
121
121
|
except Exception:
|
|
122
122
|
self.tracer.exception("Error when closing Docker client")
|
|
123
123
|
|
|
124
|
+
async def main(self):
|
|
125
|
+
"""
|
|
126
|
+
Override MqttBase.main to run task checker as a background task
|
|
127
|
+
alongside the MQTT message loop.
|
|
128
|
+
"""
|
|
129
|
+
self.tracer.debug("Starting background task checker")
|
|
130
|
+
checker = asyncio.create_task(self._ping_loop())
|
|
131
|
+
try:
|
|
132
|
+
await MqttBase.main(self)
|
|
133
|
+
finally:
|
|
134
|
+
checker.cancel()
|
|
135
|
+
try:
|
|
136
|
+
await checker
|
|
137
|
+
except asyncio.CancelledError:
|
|
138
|
+
pass
|
|
139
|
+
|
|
124
140
|
async def loop_tasks(self):
|
|
125
141
|
"""
|
|
126
|
-
|
|
142
|
+
Delegate to base class MQTT loop (subscribe + publish only).
|
|
143
|
+
Task checking runs independently in the background via main().
|
|
144
|
+
"""
|
|
145
|
+
await MqttBase.loop_tasks(self)
|
|
146
|
+
|
|
147
|
+
async def _ping_loop(self):
|
|
148
|
+
"""
|
|
149
|
+
Background loop that periodically checks task statuses and
|
|
150
|
+
sends heartbeats. Runs independently of the MQTT connection state.
|
|
127
151
|
"""
|
|
128
152
|
while True:
|
|
129
|
-
# Wait until we either (1) receive a message or (2) publish a message
|
|
130
153
|
try:
|
|
131
|
-
|
|
132
|
-
publish_task = asyncio.create_task(self.queue.get())
|
|
133
|
-
ping_task = asyncio.create_task(self._ping())
|
|
134
|
-
done, _ = await asyncio.wait(
|
|
135
|
-
(subscribe_task, publish_task, ping_task),
|
|
136
|
-
return_when=asyncio.FIRST_COMPLETED,
|
|
137
|
-
)
|
|
138
|
-
# If the asyncio.wait is cancelled, we must also cancel the queue task
|
|
154
|
+
await self._ping()
|
|
139
155
|
except asyncio.CancelledError:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
raise
|
|
144
|
-
|
|
145
|
-
# When we receive a message, process it
|
|
146
|
-
if subscribe_task in done and publish_task in done:
|
|
147
|
-
await self._subscribe_from_task(subscribe_task)
|
|
148
|
-
await self._publish_from_task(publish_task)
|
|
149
|
-
ping_task.cancel()
|
|
150
|
-
elif subscribe_task in done:
|
|
151
|
-
await self._subscribe_from_task(subscribe_task)
|
|
152
|
-
publish_task.cancel()
|
|
153
|
-
ping_task.cancel()
|
|
154
|
-
# Publish the message to its topic with specific QoS
|
|
155
|
-
elif publish_task in done:
|
|
156
|
-
await self._publish_from_task(publish_task)
|
|
157
|
-
subscribe_task.cancel()
|
|
158
|
-
ping_task.cancel()
|
|
159
|
-
# If we disconnect from the broker, stop the loop with an exception
|
|
160
|
-
elif ping_task in done:
|
|
161
|
-
ping_task.cancel()
|
|
162
|
-
publish_task.cancel()
|
|
163
|
-
subscribe_task.cancel()
|
|
164
|
-
else:
|
|
165
|
-
self.tracer.warning("Disconnected during message iteration")
|
|
166
|
-
publish_task.cancel()
|
|
167
|
-
subscribe_task.cancel()
|
|
168
|
-
ping_task.cancel()
|
|
169
|
-
raise aiomqtt.MqttError("Disconnected during message iteration")
|
|
156
|
+
break
|
|
157
|
+
except Exception:
|
|
158
|
+
self.tracer.exception("Unhandled error in ping loop, continuing...")
|
|
170
159
|
|
|
171
160
|
async def _ping(self):
|
|
172
161
|
"""
|
|
@@ -313,3 +302,32 @@ class CommandHandler(TaskManager, Collector, MqttBase):
|
|
|
313
302
|
Node ID to stop
|
|
314
303
|
"""
|
|
315
304
|
await self.stop_callback()
|
|
305
|
+
|
|
306
|
+
async def on_task_setup_failed(self, task: MqttTask, exc: Exception):
|
|
307
|
+
"""
|
|
308
|
+
Publish a FAILED task update when task setup fails.
|
|
309
|
+
|
|
310
|
+
Parameters
|
|
311
|
+
----------
|
|
312
|
+
task : MqttTask
|
|
313
|
+
Task that failed to setup
|
|
314
|
+
exc : Exception
|
|
315
|
+
The exception that caused the failure
|
|
316
|
+
"""
|
|
317
|
+
task_update = TaskUpdate(
|
|
318
|
+
swarm_id=task.swarm_id,
|
|
319
|
+
node_id=self.tracer.metadata.node_id,
|
|
320
|
+
task_id=task.task_id,
|
|
321
|
+
iteration=task.iteration,
|
|
322
|
+
circular=task.circular,
|
|
323
|
+
task_status=TaskStatus.FAILED,
|
|
324
|
+
)
|
|
325
|
+
try:
|
|
326
|
+
await self.publish_task_update(task_update)
|
|
327
|
+
self.tracer.info(
|
|
328
|
+
f"Published FAILED status for task {task.task_id} after setup failure"
|
|
329
|
+
)
|
|
330
|
+
except Exception as publish_exc:
|
|
331
|
+
self.tracer.exception(
|
|
332
|
+
f"Failed to publish FAILED status for task {task.task_id}: {repr(publish_exc)}"
|
|
333
|
+
)
|
manta_node/task_manager.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import os
|
|
2
3
|
from collections import deque
|
|
3
4
|
from datetime import datetime
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from tempfile import TemporaryDirectory
|
|
6
|
-
from typing import List, Optional, Set, Tuple
|
|
7
|
+
from typing import Dict, List, Optional, Set, Tuple
|
|
7
8
|
|
|
8
9
|
from manta_common.build.common.system import EnvId, PairId
|
|
9
10
|
from manta_common.build.common.tasks import MqttTask, TaskStatus, TaskUpdate
|
|
@@ -21,7 +22,34 @@ from .infrastructure.container.manager import ContainerManager
|
|
|
21
22
|
from .infrastructure.grpc.client import NodeClient
|
|
22
23
|
from .tasks import TaskProgression, TaskProgressions
|
|
23
24
|
|
|
24
|
-
__all__ = ["TaskManager"]
|
|
25
|
+
__all__ = ["TaskManager", "build_image_env", "IMAGE_ENV_ALLOWLIST_VAR"]
|
|
26
|
+
|
|
27
|
+
# Name of the node-environment variable holding the comma-separated allowlist of
|
|
28
|
+
# env vars to forward into image-mode containers. Set via the manta-node systemd
|
|
29
|
+
# EnvironmentFile. Empty/unset => only the reserved keys are injected.
|
|
30
|
+
IMAGE_ENV_ALLOWLIST_VAR = "MANTA_IMAGE_ENV"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def build_image_env(reserved: Dict[str, str]) -> Dict[str, str]:
|
|
34
|
+
"""Build the environment for an image-mode container.
|
|
35
|
+
|
|
36
|
+
Forwards an allowlisted set of the node's own environment variables into the
|
|
37
|
+
container, merged under the caller's ``reserved`` keys — reserved keys win on
|
|
38
|
+
collision so a node env var can never spoof e.g. ``TASK_ID``.
|
|
39
|
+
|
|
40
|
+
The allowlist is a comma-separated list of variable names read from
|
|
41
|
+
``MANTA_IMAGE_ENV`` in the node's own environment (typically populated by the
|
|
42
|
+
manta-node systemd ``EnvironmentFile``). Names absent from the node
|
|
43
|
+
environment are skipped. When ``MANTA_IMAGE_ENV`` is unset or empty, only the
|
|
44
|
+
reserved keys are injected (prior behaviour).
|
|
45
|
+
"""
|
|
46
|
+
names = [
|
|
47
|
+
name.strip()
|
|
48
|
+
for name in os.environ.get(IMAGE_ENV_ALLOWLIST_VAR, "").split(",")
|
|
49
|
+
if name.strip()
|
|
50
|
+
]
|
|
51
|
+
forwarded = {name: os.environ[name] for name in names if name in os.environ}
|
|
52
|
+
return {**forwarded, **reserved}
|
|
25
53
|
|
|
26
54
|
|
|
27
55
|
class TaskManager(ContainerManager, ImageExecutor):
|
|
@@ -233,10 +261,12 @@ class TaskManager(ContainerManager, ImageExecutor):
|
|
|
233
261
|
swarm_id=swarm_id,
|
|
234
262
|
task_id=task_id,
|
|
235
263
|
image=task.image,
|
|
236
|
-
environment=
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
264
|
+
environment=build_image_env(
|
|
265
|
+
{
|
|
266
|
+
"TASK_ID": task_id.xid,
|
|
267
|
+
"SWARM_ID": swarm_id.xid,
|
|
268
|
+
}
|
|
269
|
+
),
|
|
240
270
|
)
|
|
241
271
|
self.image_task_ids.add((swarm_id.tid, task_id.tid))
|
|
242
272
|
else:
|
|
@@ -322,12 +352,31 @@ class TaskManager(ContainerManager, ImageExecutor):
|
|
|
322
352
|
swarm_id = ID(task.swarm_id)
|
|
323
353
|
self.tracer.info(f"Deploying task {task_id}")
|
|
324
354
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
355
|
+
try:
|
|
356
|
+
if (swarm_id.tid, task_id.tid) in self.tasks:
|
|
357
|
+
await self.restart_task(self.tasks[(swarm_id.tid, task_id.tid)], task)
|
|
358
|
+
else:
|
|
359
|
+
await self.create_task(task)
|
|
360
|
+
except Exception as exc:
|
|
361
|
+
self.tracer.exception(f"Task {task_id} setup failed: {repr(exc)}")
|
|
362
|
+
await self.on_task_setup_failed(task, exc)
|
|
363
|
+
raise
|
|
329
364
|
return task_id
|
|
330
365
|
|
|
366
|
+
async def on_task_setup_failed(self, task: MqttTask, exc: Exception):
|
|
367
|
+
"""
|
|
368
|
+
Hook called when task setup fails. Override in subclasses to handle
|
|
369
|
+
failure notifications (e.g., publish MQTT update).
|
|
370
|
+
|
|
371
|
+
Parameters
|
|
372
|
+
----------
|
|
373
|
+
task : MqttTask
|
|
374
|
+
Task that failed to setup
|
|
375
|
+
exc : Exception
|
|
376
|
+
The exception that caused the failure
|
|
377
|
+
"""
|
|
378
|
+
pass
|
|
379
|
+
|
|
331
380
|
async def restart_task(self, task_progression: TaskProgression, task: MqttTask):
|
|
332
381
|
"""
|
|
333
382
|
Restart a task
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: manta_node
|
|
3
|
-
Version: 0.5b0.
|
|
3
|
+
Version: 0.5b0.dev116
|
|
4
4
|
Summary: Node side software of the Manta platform. It is responsible for the communication with the Manta Core and the execution of the tasks.
|
|
5
5
|
Author-email: Hugo Miralles <hugomiralles@manta-tech.io>, Matthew Thompson <matthew.thompson@manta-tech.io>
|
|
6
6
|
License: GNU AFFERO GENERAL PUBLIC LICENSE
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
manta_node/__init__.py,sha256=bgGWXH3yQVhNf0v1ZGUBAy5Y51fqb0Y-EaLnocbcino,153
|
|
2
2
|
manta_node/__main__.py,sha256=0VzbY1KfezaARZDuCBc7Th1GO_-BnKTK8eDEXhgP5J4,1731
|
|
3
3
|
manta_node/node_orchestrator.py,sha256=0VMBsZSMQiIe9L-Bd7Jn4avNpNyDCMa1kyPSTgRiOes,17336
|
|
4
|
-
manta_node/task_manager.py,sha256=
|
|
4
|
+
manta_node/task_manager.py,sha256=cusrPrctHC4NY6Ryb_hu9BCEVi2DUceu99H-56ztVTA,20523
|
|
5
5
|
manta_node/tasks.py,sha256=W__XjU17AX8VqOtgy8HRBmxWtVufTkFvri8s8Y3R394,6721
|
|
6
6
|
manta_node/utils.py,sha256=gkYRB7fylk1jy1sm7nKU7o8eadGxn0AX4IjCHT6Wzuw,1179
|
|
7
7
|
manta_node/cli/__init__.py,sha256=arzO3mXDJ-E2yeeytbZfN7mCswwxdQ43_QjyeL2E5yE,134
|
|
@@ -34,10 +34,10 @@ manta_node/infrastructure/metrics/__init__.py,sha256=ODGAj9s63F5jba2c3dQRMfNK6I4
|
|
|
34
34
|
manta_node/infrastructure/metrics/collector.py,sha256=nhFPAbjA04ye3UaGN95_zdpf9hLs2lrMXwOwKlFKYsI,3070
|
|
35
35
|
manta_node/infrastructure/metrics/metrics_collector.py,sha256=jhEocAPWS6xgbNvcBBiEB9cAN_9hEGssZJXoysQzWOk,10518
|
|
36
36
|
manta_node/infrastructure/mqtt/__init__.py,sha256=riXo1sXgO4YV6DHCmWRqK61BAmuGLdHiVdQFunAJadA,140
|
|
37
|
-
manta_node/infrastructure/mqtt/command_handler.py,sha256=
|
|
38
|
-
manta_node-0.5b0.
|
|
39
|
-
manta_node-0.5b0.
|
|
40
|
-
manta_node-0.5b0.
|
|
41
|
-
manta_node-0.5b0.
|
|
42
|
-
manta_node-0.5b0.
|
|
43
|
-
manta_node-0.5b0.
|
|
37
|
+
manta_node/infrastructure/mqtt/command_handler.py,sha256=mnMYIjTqfAL14k73s3ab_fBeCtgBKU9V86VWFFrZN1k,11923
|
|
38
|
+
manta_node-0.5b0.dev116.dist-info/licenses/LICENSE,sha256=P5HojupjKoAusNtHqWOvO-YFolwLFWQ7Uq0XVKK9nFQ,35291
|
|
39
|
+
manta_node-0.5b0.dev116.dist-info/METADATA,sha256=pVmpSnFpw8_1lUb7YGkQzOAW9afEaS0nMNlVRwFf9HY,44654
|
|
40
|
+
manta_node-0.5b0.dev116.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
41
|
+
manta_node-0.5b0.dev116.dist-info/entry_points.txt,sha256=C_zcw6mdmztNxVUZGLZ83UH5TwcSuqb2C-rB4e2CQoc,56
|
|
42
|
+
manta_node-0.5b0.dev116.dist-info/top_level.txt,sha256=nbT5J5WETuXM9UPoQckl29uUOq8wMYdRb9QDx5kAFeo,11
|
|
43
|
+
manta_node-0.5b0.dev116.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|