manta-node 0.6b0.dev526__py3-none-any.whl → 0.6b0.dev534__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/cli/commands/start.py +11 -3
- manta_node/infrastructure/filesystem/dataset_manager.py +4 -2
- manta_node/infrastructure/grpc/client.py +21 -1
- manta_node/infrastructure/mqtt/command_handler.py +45 -2
- manta_node/node_orchestrator.py +64 -44
- manta_node/task_manager.py +6 -0
- manta_node-0.6b0.dev534.dist-info/METADATA +115 -0
- {manta_node-0.6b0.dev526.dist-info → manta_node-0.6b0.dev534.dist-info}/RECORD +12 -12
- manta_node-0.6b0.dev526.dist-info/METADATA +0 -777
- {manta_node-0.6b0.dev526.dist-info → manta_node-0.6b0.dev534.dist-info}/WHEEL +0 -0
- {manta_node-0.6b0.dev526.dist-info → manta_node-0.6b0.dev534.dist-info}/entry_points.txt +0 -0
- {manta_node-0.6b0.dev526.dist-info → manta_node-0.6b0.dev534.dist-info}/licenses/LICENSE +0 -0
- {manta_node-0.6b0.dev526.dist-info → manta_node-0.6b0.dev534.dist-info}/top_level.txt +0 -0
manta_node/cli/commands/start.py
CHANGED
|
@@ -17,7 +17,7 @@ from manta_common.logging_config import configure_logging
|
|
|
17
17
|
from rich.console import Console
|
|
18
18
|
|
|
19
19
|
from ...infrastructure.config import NodeConfigManager
|
|
20
|
-
from ...node_orchestrator import Node
|
|
20
|
+
from ...node_orchestrator import Node, NodeFatalError
|
|
21
21
|
|
|
22
22
|
console = Console()
|
|
23
23
|
|
|
@@ -125,7 +125,15 @@ def start_node_foreground(
|
|
|
125
125
|
stop_task.cancel()
|
|
126
126
|
with contextlib.suppress(asyncio.CancelledError):
|
|
127
127
|
await stop_task
|
|
128
|
-
|
|
128
|
+
try:
|
|
129
|
+
start_task.result() # Re-raise a genuine start failure unchanged
|
|
130
|
+
except NodeFatalError:
|
|
131
|
+
# Tear down a partially-started node before surfacing the fatal error.
|
|
132
|
+
try:
|
|
133
|
+
await node.stop()
|
|
134
|
+
except Exception as exc:
|
|
135
|
+
console.print(f"[red]Error during node cleanup: {exc}[/red]")
|
|
136
|
+
raise
|
|
129
137
|
return False
|
|
130
138
|
|
|
131
139
|
# MUST precede start_task.cancel(): Node.stop() skips the DISCONNECTED
|
|
@@ -136,7 +144,7 @@ def start_node_foreground(
|
|
|
136
144
|
console.print(f"[red]Error during node cleanup: {exc}[/red]")
|
|
137
145
|
|
|
138
146
|
start_task.cancel()
|
|
139
|
-
with contextlib.suppress(asyncio.CancelledError):
|
|
147
|
+
with contextlib.suppress(asyncio.CancelledError, NodeFatalError):
|
|
140
148
|
await start_task
|
|
141
149
|
return True
|
|
142
150
|
|
|
@@ -5,12 +5,14 @@ from pathlib import Path
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
from manta_common.build.common.informations import DatasetOverviews, Overview
|
|
8
|
-
from manta_common.const import CHUNK_SIZE
|
|
9
8
|
from manta_common.errors import MantaSecurityError, MantaValidationError
|
|
10
9
|
from manta_common.traces import Tracer
|
|
11
10
|
|
|
12
11
|
__all__ = ["DatasetManager"]
|
|
13
12
|
|
|
13
|
+
# Bounded read size for streaming dataset files; not the 4 GiB message-size ceiling manta_common.CHUNK_SIZE.
|
|
14
|
+
STREAM_CHUNK_SIZE = 1024 * 1024
|
|
15
|
+
|
|
14
16
|
# File type to dtype mapping
|
|
15
17
|
DTYPES = {
|
|
16
18
|
".txt": "text",
|
|
@@ -243,7 +245,7 @@ class DatasetManager:
|
|
|
243
245
|
if not file_path.exists():
|
|
244
246
|
raise FileNotFoundError(f"File not found: {file_path}")
|
|
245
247
|
with file_path.open("rb") as f:
|
|
246
|
-
while chunk := f.read(
|
|
248
|
+
while chunk := f.read(STREAM_CHUNK_SIZE):
|
|
247
249
|
yield chunk
|
|
248
250
|
|
|
249
251
|
def _validate_file_path(self, file_path: str) -> str:
|
|
@@ -24,7 +24,7 @@ from manta_common.build.common.results import (
|
|
|
24
24
|
ResultQuery,
|
|
25
25
|
)
|
|
26
26
|
from manta_common.build.common.system import Empty, Logs, Ok
|
|
27
|
-
from manta_common.build.common.tasks import TaskScheduling
|
|
27
|
+
from manta_common.build.common.tasks import TaskScheduling, TaskUpdate
|
|
28
28
|
from manta_common.build.common.wireguard import (
|
|
29
29
|
DeregisterWireGuardNodeRequest,
|
|
30
30
|
RegisterWireGuardNodeRequest,
|
|
@@ -360,6 +360,26 @@ class NodeClient(GrpcClientBase):
|
|
|
360
360
|
"""
|
|
361
361
|
return await self.call_service_method("schedule_task", request)
|
|
362
362
|
|
|
363
|
+
async def update_task(self, request: TaskUpdate) -> Ok:
|
|
364
|
+
"""
|
|
365
|
+
Report a task status update to the manager.
|
|
366
|
+
|
|
367
|
+
This is the authoritative channel the manager's task scheduler
|
|
368
|
+
consumes; MQTT ``manager/update_task_status`` is a secondary,
|
|
369
|
+
lossy channel (refs #287).
|
|
370
|
+
|
|
371
|
+
Parameters
|
|
372
|
+
----------
|
|
373
|
+
request : TaskUpdate
|
|
374
|
+
Task status update
|
|
375
|
+
|
|
376
|
+
Returns
|
|
377
|
+
-------
|
|
378
|
+
Ok
|
|
379
|
+
Response
|
|
380
|
+
"""
|
|
381
|
+
return await self.call_service_method("update_task", request)
|
|
382
|
+
|
|
363
383
|
async def send_error(self, manta_error: MantaError):
|
|
364
384
|
"""
|
|
365
385
|
Send the error to the Manager
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import base64
|
|
2
3
|
import io
|
|
4
|
+
import json
|
|
5
|
+
import re
|
|
3
6
|
from collections.abc import AsyncIterable, Awaitable, Callable
|
|
4
7
|
from datetime import datetime, timezone
|
|
5
8
|
from logging import getLogger
|
|
@@ -12,7 +15,7 @@ from manta_common.build.common.mqtt import MqMetricsSnapshot, MqTaskUpdate
|
|
|
12
15
|
from manta_common.build.common.system import Logs
|
|
13
16
|
from manta_common.build.common.tasks import MqttTask, TaskStatus, TaskUpdate
|
|
14
17
|
from manta_common.const import CHUNK_SIZE
|
|
15
|
-
from manta_common.conversions import ID
|
|
18
|
+
from manta_common.conversions import ID, b2x
|
|
16
19
|
from manta_common.errors import (
|
|
17
20
|
MantaError,
|
|
18
21
|
MantaGRPCError,
|
|
@@ -42,6 +45,30 @@ def _is_permanent_upload_rejection(exc: Exception) -> bool:
|
|
|
42
45
|
)
|
|
43
46
|
|
|
44
47
|
|
|
48
|
+
_NODE_ID_CLAIM_PATTERN = re.compile(r"^[0-9a-fA-F]{24}$")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _node_id_from_token(secured_token: str) -> str | None:
|
|
52
|
+
"""Return the 24-hex ``node_id`` claim from a JWT payload, else None.
|
|
53
|
+
|
|
54
|
+
Reads the payload without verifying the signature: the node trusts the
|
|
55
|
+
manager-minted token it already holds. Never logs the token.
|
|
56
|
+
"""
|
|
57
|
+
try:
|
|
58
|
+
_, payload, _ = secured_token.split(".", 2)
|
|
59
|
+
except ValueError:
|
|
60
|
+
return None
|
|
61
|
+
try:
|
|
62
|
+
padded = payload + "=" * (-len(payload) % 4)
|
|
63
|
+
claims = json.loads(base64.urlsafe_b64decode(padded))
|
|
64
|
+
except (ValueError, UnicodeDecodeError):
|
|
65
|
+
return None
|
|
66
|
+
node_id = claims.get("node_id") if isinstance(claims, dict) else None
|
|
67
|
+
if not isinstance(node_id, str) or not _NODE_ID_CLAIM_PATTERN.fullmatch(node_id):
|
|
68
|
+
return None
|
|
69
|
+
return node_id
|
|
70
|
+
|
|
71
|
+
|
|
45
72
|
class CommandHandler(Collector, MqttBase):
|
|
46
73
|
"""
|
|
47
74
|
CommandHandler class to handle commands from the Manager.
|
|
@@ -97,12 +124,17 @@ class CommandHandler(Collector, MqttBase):
|
|
|
97
124
|
self.max_concurrent_tasks = max_concurrent_tasks
|
|
98
125
|
self.task_timeout = task_timeout
|
|
99
126
|
|
|
127
|
+
# Never expose the cluster JWT as the client id: derive it from the node_id claim.
|
|
128
|
+
client_id = ID(
|
|
129
|
+
_node_id_from_token(node_client.secured_token)
|
|
130
|
+
or b2x(node_client.secured_token.encode())
|
|
131
|
+
)
|
|
100
132
|
MqttBase.__init__(
|
|
101
133
|
self,
|
|
102
134
|
mqtt_broker_host=mqtt_broker_host,
|
|
103
135
|
mqtt_broker_port=mqtt_broker_port,
|
|
104
136
|
topics=topics,
|
|
105
|
-
client_id=
|
|
137
|
+
client_id=client_id,
|
|
106
138
|
ssl_cert_folder=ssl_cert_folder,
|
|
107
139
|
)
|
|
108
140
|
|
|
@@ -171,6 +203,9 @@ class CommandHandler(Collector, MqttBase):
|
|
|
171
203
|
async def get_task_logs(self, task_progression):
|
|
172
204
|
return await self._task_manager.get_task_logs(task_progression)
|
|
173
205
|
|
|
206
|
+
async def get_image_task_status(self, swarm_id, task_id):
|
|
207
|
+
return await self._task_manager.get_image_task_status(swarm_id, task_id)
|
|
208
|
+
|
|
174
209
|
async def restart_container(self, swarm_id, task_id):
|
|
175
210
|
return await self._task_manager.restart_container(swarm_id, task_id)
|
|
176
211
|
|
|
@@ -539,6 +574,14 @@ class CommandHandler(Collector, MqttBase):
|
|
|
539
574
|
"manager/update_task_status",
|
|
540
575
|
MqTaskUpdate(token=self.node_client.secured_token, update=task_update),
|
|
541
576
|
)
|
|
577
|
+
# Also report via the gRPC UpdateTask RPC the manager's scheduler consumes.
|
|
578
|
+
try:
|
|
579
|
+
await self.node_client.update_task(task_update)
|
|
580
|
+
except Exception:
|
|
581
|
+
self.tracer.exception(
|
|
582
|
+
f"Failed to report task status via gRPC for task "
|
|
583
|
+
f"{ID(task_update.task_id)}"
|
|
584
|
+
)
|
|
542
585
|
|
|
543
586
|
async def send_task_logs(self, date: datetime, processed_logs: bytes):
|
|
544
587
|
"""
|
manta_node/node_orchestrator.py
CHANGED
|
@@ -2,7 +2,6 @@ import asyncio
|
|
|
2
2
|
import socket
|
|
3
3
|
import ssl
|
|
4
4
|
import struct
|
|
5
|
-
import sys
|
|
6
5
|
import uuid
|
|
7
6
|
from logging import getLogger
|
|
8
7
|
from pathlib import Path
|
|
@@ -36,7 +35,16 @@ from .infrastructure.mqtt.command_handler import CommandHandler
|
|
|
36
35
|
from .infrastructure.process.executor import ProcessExecutor
|
|
37
36
|
from .task_manager import TaskManager
|
|
38
37
|
|
|
39
|
-
__all__ = ["Node"]
|
|
38
|
+
__all__ = ["Node", "NodeFatalError"]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class NodeFatalError(Exception):
|
|
42
|
+
"""Raised by Node.start()'s fatal paths instead of sys.exit() from inside a task.
|
|
43
|
+
|
|
44
|
+
The CLI maps it to a non-zero exit code and keeps shutdown ordering under
|
|
45
|
+
its own control.
|
|
46
|
+
"""
|
|
47
|
+
|
|
40
48
|
|
|
41
49
|
# Docker-specific exceptions. Imported lazily so a light node (no Docker
|
|
42
50
|
# package installed) can import this module and run in process mode without
|
|
@@ -184,6 +192,8 @@ class Node:
|
|
|
184
192
|
"""
|
|
185
193
|
|
|
186
194
|
__slots__ = [
|
|
195
|
+
"_stop_done",
|
|
196
|
+
"_stop_lock",
|
|
187
197
|
"cert_folder",
|
|
188
198
|
"command_handler",
|
|
189
199
|
"command_loop",
|
|
@@ -262,6 +272,10 @@ class Node:
|
|
|
262
272
|
self.command_loop = None
|
|
263
273
|
self.light_server_task = None
|
|
264
274
|
|
|
275
|
+
# Serialise teardown so concurrent stop() callers wait and then no-op.
|
|
276
|
+
self._stop_lock = asyncio.Lock()
|
|
277
|
+
self._stop_done = False
|
|
278
|
+
|
|
265
279
|
# Register configuration observer for runtime updates
|
|
266
280
|
self.config_manager.register_observer(self._on_config_change)
|
|
267
281
|
|
|
@@ -365,11 +379,11 @@ class Node:
|
|
|
365
379
|
Then, it will check if the manager is available by calling
|
|
366
380
|
the `is_available` function of the node client.
|
|
367
381
|
If the manager is not available, it will log an error and
|
|
368
|
-
|
|
382
|
+
raise a :code:`NodeFatalError` with a non-zero status report.
|
|
369
383
|
|
|
370
384
|
Raises
|
|
371
385
|
------
|
|
372
|
-
|
|
386
|
+
NodeFatalError
|
|
373
387
|
If the manager is not available.
|
|
374
388
|
"""
|
|
375
389
|
# Log endpoint details before attempting connection
|
|
@@ -400,11 +414,13 @@ class Node:
|
|
|
400
414
|
f" 3. Verify JWT_SECRET matches between node and manager\n"
|
|
401
415
|
f" 4. Check network connectivity to {self.config.network.manager_host}:{self.config.network.manager_port}\n"
|
|
402
416
|
)
|
|
403
|
-
|
|
417
|
+
raise NodeFatalError(troubleshooting) from error
|
|
404
418
|
except MantaError as manta_error:
|
|
405
419
|
tls_error = _find_tls_error(manta_error)
|
|
406
420
|
if tls_error is not None:
|
|
407
|
-
|
|
421
|
+
raise NodeFatalError(
|
|
422
|
+
self._tls_troubleshooting(manta_error, tls_error)
|
|
423
|
+
) from manta_error
|
|
408
424
|
|
|
409
425
|
message = f"Manager authentication failed at {endpoint}: {manta_error}"
|
|
410
426
|
self.tracer.exception(message)
|
|
@@ -418,11 +434,11 @@ class Node:
|
|
|
418
434
|
f" - Verify secured_token in node config matches manager's JWT_SECRET\n"
|
|
419
435
|
f" - Check token format: should be a valid JWT with 3 parts (header.payload.signature)\n"
|
|
420
436
|
)
|
|
421
|
-
|
|
437
|
+
raise NodeFatalError(troubleshooting) from manta_error
|
|
422
438
|
except Exception as exc:
|
|
423
439
|
message = f"Unknown error connecting to {endpoint}: {exc}"
|
|
424
440
|
self.tracer.exception(message)
|
|
425
|
-
|
|
441
|
+
raise NodeFatalError(f"[ERROR] {message}") from exc
|
|
426
442
|
|
|
427
443
|
async def start(self) -> None:
|
|
428
444
|
"""
|
|
@@ -436,7 +452,7 @@ class Node:
|
|
|
436
452
|
|
|
437
453
|
Raises
|
|
438
454
|
------
|
|
439
|
-
|
|
455
|
+
NodeFatalError
|
|
440
456
|
If MQTT client unreacheable or gRPC error occurs
|
|
441
457
|
"""
|
|
442
458
|
await self.check_manager_availability()
|
|
@@ -542,14 +558,16 @@ class Node:
|
|
|
542
558
|
await asyncio.gather(self.command_loop, self.light_server_task)
|
|
543
559
|
except ConnectionRefusedError:
|
|
544
560
|
self.tracer.exception("Unable to start and connect to MQTT Message Broker")
|
|
545
|
-
|
|
561
|
+
raise NodeFatalError(
|
|
562
|
+
"[ERROR] Unable to start and connect to MQTT Message Broker"
|
|
563
|
+
) from None
|
|
546
564
|
except GRPCError:
|
|
547
565
|
self.tracer.exception("Unable to register to the Manager")
|
|
548
|
-
|
|
566
|
+
raise NodeFatalError("[ERROR] Unable to register to the Manager") from None
|
|
549
567
|
except OSError as os_error:
|
|
550
568
|
self.tracer.exception(f"OS error: {os_error}")
|
|
551
569
|
await self.stop()
|
|
552
|
-
|
|
570
|
+
raise NodeFatalError(f"[ERROR] OS error: {os_error}") from os_error
|
|
553
571
|
except Exception as exc:
|
|
554
572
|
# Docker-specific failures are only meaningful in container mode.
|
|
555
573
|
# In process mode they would mask unrelated OS errors, so they are
|
|
@@ -561,39 +579,41 @@ class Node:
|
|
|
561
579
|
):
|
|
562
580
|
self.tracer.exception(f"Docker is not available: {exc}")
|
|
563
581
|
await self.stop()
|
|
564
|
-
|
|
582
|
+
raise NodeFatalError(f"[ERROR] Docker is not available: {exc}") from exc
|
|
565
583
|
self.tracer.exception(f"Unknown error: {exc}")
|
|
566
584
|
await self.stop()
|
|
567
|
-
|
|
585
|
+
raise NodeFatalError(f"[ERROR] Unknown error: {exc}") from exc
|
|
568
586
|
|
|
569
587
|
async def stop(self) -> None:
|
|
570
|
-
"""
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
588
|
+
"""Stop the node"""
|
|
589
|
+
async with self._stop_lock:
|
|
590
|
+
if self._stop_done:
|
|
591
|
+
return
|
|
592
|
+
try:
|
|
593
|
+
self.tracer.info("Stopping node")
|
|
594
|
+
if (
|
|
595
|
+
self.command_handler is not None
|
|
596
|
+
and self.command_loop is not None
|
|
597
|
+
and not self.command_loop.cancelled()
|
|
598
|
+
):
|
|
599
|
+
self.tracer.info("Publishing node status to manager")
|
|
600
|
+
await self.command_handler.client.publish(
|
|
601
|
+
"manager/set_node_status",
|
|
602
|
+
NodeStatus(
|
|
603
|
+
status=NodeStatusEnum.DISCONNECTED
|
|
604
|
+
).SerializeToString(),
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
if self.command_handler is not None:
|
|
608
|
+
self.tracer.info("Cleaning up command handler")
|
|
609
|
+
await self.command_handler.clean()
|
|
610
|
+
finally:
|
|
611
|
+
# Cancel the loops even if publish/clean raised, or they leak.
|
|
612
|
+
if self.command_loop is not None:
|
|
613
|
+
self.tracer.info("Stopping command loop")
|
|
614
|
+
self.command_loop.cancel()
|
|
615
|
+
|
|
616
|
+
if self.light_server_task is not None:
|
|
617
|
+
self.tracer.info("Stopping light server task")
|
|
618
|
+
self.light_server_task.cancel()
|
|
619
|
+
self._stop_done = True
|
manta_node/task_manager.py
CHANGED
|
@@ -302,9 +302,15 @@ class TaskManager:
|
|
|
302
302
|
return updated
|
|
303
303
|
|
|
304
304
|
async def get_task_status(self, swarm_id: ID, task_id: ID) -> TaskStatus:
|
|
305
|
+
if (swarm_id.tid, task_id.tid) in self.image_task_ids:
|
|
306
|
+
return await self.get_image_task_status(swarm_id, task_id)
|
|
305
307
|
status = await self._executor.get_status(swarm_id, task_id)
|
|
306
308
|
return TaskLifecycleService.calculate_status_from_container(status)
|
|
307
309
|
|
|
310
|
+
async def get_image_task_status(self, swarm_id: ID, task_id: ID) -> TaskStatus:
|
|
311
|
+
"""Exit-code-aware status for image-mode tasks (no SDK inside the container)."""
|
|
312
|
+
return await self._executor.get_image_task_status(swarm_id, task_id)
|
|
313
|
+
|
|
308
314
|
async def from_pending_to_running(self, task_progression: TaskProgression) -> bool:
|
|
309
315
|
"""
|
|
310
316
|
Change a task progression from pending status (supposed) to running status
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: manta_node
|
|
3
|
+
Version: 0.6b0.dev534
|
|
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
|
+
Author-email: Hugo Miralles <hugo.miralles@manta-tech.io>, Matthew Thompson <matthew.thompson@manta-tech.io>
|
|
6
|
+
License-Expression: AGPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/mantatech/manta-node-python
|
|
8
|
+
Project-URL: Repository, https://github.com/mantatech/manta-node-python
|
|
9
|
+
Classifier: Programming Language :: JavaScript
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: grpcio-tools
|
|
20
|
+
Requires-Dist: betterproto[compiler]==2.0.0b7
|
|
21
|
+
Requires-Dist: pydantic<3.0.0,>=2.12.5
|
|
22
|
+
Requires-Dist: docker
|
|
23
|
+
Requires-Dist: rich
|
|
24
|
+
Requires-Dist: cryptography>=50.0.0
|
|
25
|
+
Requires-Dist: blake3
|
|
26
|
+
Requires-Dist: aiodocker>=0.26.0
|
|
27
|
+
Requires-Dist: pyyaml
|
|
28
|
+
Requires-Dist: psutil
|
|
29
|
+
Requires-Dist: msgpack
|
|
30
|
+
Requires-Dist: aiomqtt<3.0,>=2.5.1
|
|
31
|
+
Requires-Dist: manta-common-core<=0.6b0,>=0.6b0.dev0
|
|
32
|
+
Requires-Dist: toml
|
|
33
|
+
Requires-Dist: tomli; python_version < "3.11"
|
|
34
|
+
Requires-Dist: tomli-w
|
|
35
|
+
Requires-Dist: keyring
|
|
36
|
+
Requires-Dist: wireguard-tools
|
|
37
|
+
Requires-Dist: pyroute2
|
|
38
|
+
Requires-Dist: icmplib
|
|
39
|
+
Provides-Extra: test
|
|
40
|
+
Requires-Dist: pytest; extra == "test"
|
|
41
|
+
Requires-Dist: pytest-asyncio; extra == "test"
|
|
42
|
+
Provides-Extra: docs
|
|
43
|
+
Requires-Dist: furo; extra == "docs"
|
|
44
|
+
Provides-Extra: code-analytics
|
|
45
|
+
Requires-Dist: ruff; extra == "code-analytics"
|
|
46
|
+
Requires-Dist: pytest-cov; extra == "code-analytics"
|
|
47
|
+
Requires-Dist: coverage-badge; extra == "code-analytics"
|
|
48
|
+
Provides-Extra: all
|
|
49
|
+
Requires-Dist: manta_node[code-analytics,docs,test]; extra == "all"
|
|
50
|
+
Dynamic: license-file
|
|
51
|
+
|
|
52
|
+
# Manta Node Python
|
|
53
|
+
|
|
54
|
+

|
|
55
|
+

|
|
56
|
+

|
|
57
|
+

|
|
58
|
+
|
|
59
|
+
## Overview
|
|
60
|
+
|
|
61
|
+
Distributed edge execution agent for the Manta platform. Manages containerized task execution on compute nodes and provides local services to running tasks.
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Install
|
|
67
|
+
pip install -e ".[all]"
|
|
68
|
+
|
|
69
|
+
# Run node
|
|
70
|
+
python -m manta_node --manager-host localhost --manager-port 50051 --token <jwt_token>
|
|
71
|
+
|
|
72
|
+
# Run multiple nodes
|
|
73
|
+
python -c "from manta_node import start_nodes; import asyncio; asyncio.run(start_nodes(3, 'localhost', 50051, '<jwt_token>'))"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Documentation
|
|
77
|
+
|
|
78
|
+
📚 **Full documentation is available at [docs.manta-tech.io](https://docs.manta-tech.io/)**
|
|
79
|
+
|
|
80
|
+
- [Node Operator Guide](https://docs.manta-tech.io/node-guide/index.html)
|
|
81
|
+
- [Node Configuration](https://docs.manta-tech.io/node-guide/configuration/index.html)
|
|
82
|
+
- [Getting Started](https://docs.manta-tech.io/getting-started/index.html)
|
|
83
|
+
- [Platform Overview](https://docs.manta-tech.io/core-concepts/index.html)
|
|
84
|
+
|
|
85
|
+
For developers working on this component:
|
|
86
|
+
|
|
87
|
+
- **[Node Architecture](https://github.com/mantatech/manta-deploy/blob/main/docs/components/node/ARCHITECTURE.md)** - Container orchestration, service implementation (lives in `mantatech/manta-deploy` per [ADR-0013](https://github.com/mantatech/manta-deploy/blob/main/docs/adr/0013-documentation-organization.md))
|
|
88
|
+
- **[CLAUDE.md](./CLAUDE.md)** - Development guidelines
|
|
89
|
+
|
|
90
|
+
## Architecture
|
|
91
|
+
|
|
92
|
+
- **Container Management**: Docker-based task execution
|
|
93
|
+
- **gRPC Services**: LocalServicer and WorldServicer for task communication
|
|
94
|
+
- **MQTT Integration**: Asynchronous command reception
|
|
95
|
+
- **Resource Monitoring**: Real-time metrics collection
|
|
96
|
+
|
|
97
|
+
## Testing
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
python -m pytest tests/ --cov=manta_node
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Docker Setup
|
|
104
|
+
|
|
105
|
+
For Linux users encountering permission issues:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Follow Docker post-installation steps
|
|
109
|
+
# https://docs.docker.com/engine/install/linux-postinstall/
|
|
110
|
+
sudo usermod -aG docker $USER
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
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=SjrJyuMg_ZFKV6WOVe3MrtWM8Qmu065dkKZYl4n2YLw,1665
|
|
3
|
-
manta_node/node_orchestrator.py,sha256=
|
|
4
|
-
manta_node/task_manager.py,sha256=
|
|
3
|
+
manta_node/node_orchestrator.py,sha256=Y1qOlyrXxk3znBidUagradrjiEVdl8BmgGRmtUYHTL8,24195
|
|
4
|
+
manta_node/task_manager.py,sha256=l8EBQx5Spn2UISUK61WWqanefJF1_p2qGpxP8BeG_7Q,33428
|
|
5
5
|
manta_node/tasks.py,sha256=o837Hi-KFVJg9LF5iOtE8oD6UuTOfrEPWR-JOdhRGeY,6983
|
|
6
6
|
manta_node/utils.py,sha256=Hw4Iw2jYQYsfVLVJuBMt-_rMFBeml3nCDE2jiiXc8YM,1155
|
|
7
7
|
manta_node/cli/__init__.py,sha256=arzO3mXDJ-E2yeeytbZfN7mCswwxdQ43_QjyeL2E5yE,134
|
|
@@ -13,7 +13,7 @@ manta_node/cli/commands/cluster.py,sha256=kUVopCN4xkEk-IR1aKpr7bfBSQEtG6pEj_hlWn
|
|
|
13
13
|
manta_node/cli/commands/config.py,sha256=BiarCD0S8rcNYbio4RLzDvUS7A50s5dd5F4TiZqLR6M,16577
|
|
14
14
|
manta_node/cli/commands/logs.py,sha256=cXOSdwhDIJAiKMmJYdzrnpapIxudxVdMzYT-KAWDKVk,5259
|
|
15
15
|
manta_node/cli/commands/network.py,sha256=QqncD6vbNlg65ptQdFYexEUMALQm8UwcftbW3sfQrjw,42070
|
|
16
|
-
manta_node/cli/commands/start.py,sha256=
|
|
16
|
+
manta_node/cli/commands/start.py,sha256=8qpOTv4LsCAMaygmPyhC9ZqlH8rkFPko-u2bVYTvyIo,18771
|
|
17
17
|
manta_node/cli/commands/status.py,sha256=FhlJuFxIRzxv9y8jUJRuWaCLfDBsGBqv6DCHUFQta3A,6401
|
|
18
18
|
manta_node/cli/commands/stop.py,sha256=8on7NqqaBeHRcbrZ5pGJ3Ms_6E_HNcF22mZ908JVu4A,7695
|
|
19
19
|
manta_node/domain/__init__.py,sha256=uyJXDf0QgfGCsXLMggqKCB5QV9oneYJ4iM-REaJGXDM,217
|
|
@@ -30,16 +30,16 @@ manta_node/infrastructure/container/executor.py,sha256=rl8eDyTVvoPJGnzA8XbY3ee_K
|
|
|
30
30
|
manta_node/infrastructure/container/image_executor.py,sha256=ueLYGlMt1xuYCsuwYqf9HcitWgrWfieY0xq1vXZfCco,9987
|
|
31
31
|
manta_node/infrastructure/container/manager.py,sha256=wJWBU160S6R4-R3B4eIg6p7ik2hCCeAll43wGs8Z8SY,15506
|
|
32
32
|
manta_node/infrastructure/filesystem/__init__.py,sha256=nf6Fakp3wuFKjSAm_oihI6WE_yyUY6Uelr7A1GwSvXs,145
|
|
33
|
-
manta_node/infrastructure/filesystem/dataset_manager.py,sha256=
|
|
33
|
+
manta_node/infrastructure/filesystem/dataset_manager.py,sha256=sW7vPKYBLjwbtju1yzFEob9GlY1v0VI-xu6gxF9KM90,15075
|
|
34
34
|
manta_node/infrastructure/grpc/__init__.py,sha256=6I-yzQM42gdBfcZ1FPVEKF2-pgJMdW-UssDHgz2EFcc,254
|
|
35
|
-
manta_node/infrastructure/grpc/client.py,sha256=
|
|
35
|
+
manta_node/infrastructure/grpc/client.py,sha256=f8z61BYfBChRW0IT5HKbIxypRfsxfCpTkX0Xsmw_vq8,13737
|
|
36
36
|
manta_node/infrastructure/grpc/local_servicer.py,sha256=9PFAwpKKFGy3P7HZfx2BHwhOgZhWwkwEja-eUTnMfpw,4217
|
|
37
37
|
manta_node/infrastructure/grpc/world_servicer.py,sha256=nmyrMzYxzGDlZ1-oOeyALgd76mbeOmAOsjzoVN6f0SA,8222
|
|
38
38
|
manta_node/infrastructure/metrics/__init__.py,sha256=ODGAj9s63F5jba2c3dQRMfNK6I4WvMdcJUS_xApUN5Y,272
|
|
39
39
|
manta_node/infrastructure/metrics/collector.py,sha256=9ESw__Pc2tV6ldG5yhos2IS7T5Td59KxTIwDRZc_G4w,3067
|
|
40
40
|
manta_node/infrastructure/metrics/metrics_collector.py,sha256=TI_9tRoIUunGx_BstVavK6VElig7Wc5mJ0kB4bFumVs,11048
|
|
41
41
|
manta_node/infrastructure/mqtt/__init__.py,sha256=riXo1sXgO4YV6DHCmWRqK61BAmuGLdHiVdQFunAJadA,140
|
|
42
|
-
manta_node/infrastructure/mqtt/command_handler.py,sha256=
|
|
42
|
+
manta_node/infrastructure/mqtt/command_handler.py,sha256=WZgcOyzPJ5xgC6rrNumej3dd1LYDaWswlHWNSZJ9fYg,24528
|
|
43
43
|
manta_node/infrastructure/network/__init__.py,sha256=On3XLgycFQoc-fBCa5hFuFDfEJjmFy7Wl2CK3D2LkdI,478
|
|
44
44
|
manta_node/infrastructure/network/event_registry.py,sha256=iWEfUJRzDnWjOrDinpYqVyIUpGKJjp7RZkjMDPfpplM,1661
|
|
45
45
|
manta_node/infrastructure/network/factory.py,sha256=-LvHbBp5nBpQ7fyQT1T0pvK3aczZqWc9QNDW37brBeU,3897
|
|
@@ -47,9 +47,9 @@ manta_node/infrastructure/network/protocols.py,sha256=U9AZM_R3Qizz0U-mIwC79jFDGW
|
|
|
47
47
|
manta_node/infrastructure/network/wireguard_provider.py,sha256=1U4wbpxG3fQng6d5drqC0clzw26iLSYBlUWRbMyR00c,37923
|
|
48
48
|
manta_node/infrastructure/process/__init__.py,sha256=MpKkA3k-6ND-Nb7uyzrpw8MdQssQQ6NaiUTLuLnzNbE,53
|
|
49
49
|
manta_node/infrastructure/process/executor.py,sha256=Ab1XvNuelEGCD5KdZLgYYnpjYZ5dpKRKOzRWL68AxQg,11601
|
|
50
|
-
manta_node-0.6b0.
|
|
51
|
-
manta_node-0.6b0.
|
|
52
|
-
manta_node-0.6b0.
|
|
53
|
-
manta_node-0.6b0.
|
|
54
|
-
manta_node-0.6b0.
|
|
55
|
-
manta_node-0.6b0.
|
|
50
|
+
manta_node-0.6b0.dev534.dist-info/licenses/LICENSE,sha256=9mBgLhiB9goQPQjg9XQJ6FjgVxYw9Q8lyOZYkPGwF_c,34484
|
|
51
|
+
manta_node-0.6b0.dev534.dist-info/METADATA,sha256=SXyXZeplD4gYLl_9jqkeXOgv5Q4tGUjanKSFh5WQMpc,4213
|
|
52
|
+
manta_node-0.6b0.dev534.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
53
|
+
manta_node-0.6b0.dev534.dist-info/entry_points.txt,sha256=C_zcw6mdmztNxVUZGLZ83UH5TwcSuqb2C-rB4e2CQoc,56
|
|
54
|
+
manta_node-0.6b0.dev534.dist-info/top_level.txt,sha256=nbT5J5WETuXM9UPoQckl29uUOq8wMYdRb9QDx5kAFeo,11
|
|
55
|
+
manta_node-0.6b0.dev534.dist-info/RECORD,,
|