manta-node 0.5b0.dev81__py3-none-any.whl → 0.5b0.dev83__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/domain/task_lifecycle.py +7 -0
- manta_node/infrastructure/container/__init__.py +2 -0
- manta_node/infrastructure/container/image_executor.py +86 -0
- manta_node/task_manager.py +75 -58
- {manta_node-0.5b0.dev81.dist-info → manta_node-0.5b0.dev83.dist-info}/METADATA +1 -1
- {manta_node-0.5b0.dev81.dist-info → manta_node-0.5b0.dev83.dist-info}/RECORD +10 -9
- {manta_node-0.5b0.dev81.dist-info → manta_node-0.5b0.dev83.dist-info}/WHEEL +0 -0
- {manta_node-0.5b0.dev81.dist-info → manta_node-0.5b0.dev83.dist-info}/entry_points.txt +0 -0
- {manta_node-0.5b0.dev81.dist-info → manta_node-0.5b0.dev83.dist-info}/licenses/LICENSE +0 -0
- {manta_node-0.5b0.dev81.dist-info → manta_node-0.5b0.dev83.dist-info}/top_level.txt +0 -0
|
@@ -63,6 +63,13 @@ class TaskLifecycleService:
|
|
|
63
63
|
}
|
|
64
64
|
return status_map.get(container_status, TaskStatus.FAILED)
|
|
65
65
|
|
|
66
|
+
@staticmethod
|
|
67
|
+
def status_from_exit(container_status: str, exit_code: int) -> TaskStatus:
|
|
68
|
+
"""For image-mode tasks: exit code is the authoritative completion signal."""
|
|
69
|
+
if container_status == "exited":
|
|
70
|
+
return TaskStatus.COMPLETED if exit_code == 0 else TaskStatus.FAILED
|
|
71
|
+
return TaskLifecycleService.calculate_status_from_container(container_status)
|
|
72
|
+
|
|
66
73
|
@staticmethod
|
|
67
74
|
def should_restart_task(
|
|
68
75
|
current_iteration: int,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""Container infrastructure - Docker runtime management."""
|
|
2
2
|
|
|
3
3
|
from .docker_adapter import DockerRaiser
|
|
4
|
+
from .image_executor import ImageExecutor
|
|
4
5
|
from .manager import ContainerManager
|
|
5
6
|
|
|
6
7
|
__all__ = [
|
|
7
8
|
"ContainerManager",
|
|
8
9
|
"DockerRaiser",
|
|
10
|
+
"ImageExecutor",
|
|
9
11
|
]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Dict, Optional, Tuple
|
|
3
|
+
|
|
4
|
+
from aiodocker.docker import DockerContainer
|
|
5
|
+
|
|
6
|
+
from manta_common.build.common.tasks import TaskStatus
|
|
7
|
+
from manta_common.conversions import ID
|
|
8
|
+
from manta_common.errors import MantaDockerError
|
|
9
|
+
from manta_common.traces import Tracer
|
|
10
|
+
|
|
11
|
+
from ...domain.task_lifecycle import TaskLifecycleService
|
|
12
|
+
from .docker_adapter import DockerRaiser
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ImageExecutor(DockerRaiser):
|
|
16
|
+
"""
|
|
17
|
+
Runs plain Docker images with native ENTRYPOINT/CMD.
|
|
18
|
+
No zipapp, no manta SDK, no gRPC inside the container.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
tracer: Tracer
|
|
22
|
+
containers: Dict[Tuple[ID, ID], DockerContainer]
|
|
23
|
+
|
|
24
|
+
async def run_image(
|
|
25
|
+
self,
|
|
26
|
+
swarm_id: ID,
|
|
27
|
+
task_id: ID,
|
|
28
|
+
image: str,
|
|
29
|
+
environment: Optional[dict] = None,
|
|
30
|
+
) -> datetime:
|
|
31
|
+
"""
|
|
32
|
+
Pull (if not present locally) and start a Docker image with its native ENTRYPOINT.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
datetime
|
|
37
|
+
Container start time
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
await self.async_client.images.inspect(image)
|
|
41
|
+
except Exception:
|
|
42
|
+
self.tracer.info(f"Image {image} not found locally, pulling...")
|
|
43
|
+
await self.pull_image_or_raise(image)
|
|
44
|
+
|
|
45
|
+
env = [f"{k}={v}" for k, v in environment.items()] if environment else None
|
|
46
|
+
config = {
|
|
47
|
+
"Image": image,
|
|
48
|
+
"HostConfig": {
|
|
49
|
+
"NetworkMode": "host",
|
|
50
|
+
"ExtraHosts": ["host.docker.internal:host-gateway"],
|
|
51
|
+
},
|
|
52
|
+
"Env": env,
|
|
53
|
+
"Detach": True,
|
|
54
|
+
"LogConfig": {
|
|
55
|
+
"type": "json-file",
|
|
56
|
+
"config": {"max-size": "10m", "max-file": "3"},
|
|
57
|
+
},
|
|
58
|
+
"Labels": {
|
|
59
|
+
"com.docker.compose.project": "manta_tasks",
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
start_time = datetime.now()
|
|
64
|
+
self.tracer.debug(f"Starting image {image} for task {task_id}")
|
|
65
|
+
self.containers[(swarm_id, task_id)] = await self.run_or_raise(config)
|
|
66
|
+
self.tracer.info(f"Task {task_id} image container started.")
|
|
67
|
+
return start_time
|
|
68
|
+
|
|
69
|
+
async def get_image_task_status(self, swarm_id: ID, task_id: ID) -> TaskStatus:
|
|
70
|
+
"""
|
|
71
|
+
Exit-code-aware status for image-mode tasks (no SDK inside the container).
|
|
72
|
+
"""
|
|
73
|
+
container = self.containers.get((swarm_id, task_id))
|
|
74
|
+
if container is None:
|
|
75
|
+
raise MantaDockerError("Container not found", metadata=self.tracer.metadata)
|
|
76
|
+
|
|
77
|
+
inspect = await container.show()
|
|
78
|
+
container_status = inspect["State"]["Status"]
|
|
79
|
+
exit_code = inspect["State"].get("ExitCode", 0)
|
|
80
|
+
|
|
81
|
+
status = TaskLifecycleService.status_from_exit(container_status, exit_code)
|
|
82
|
+
self.tracer.debug(
|
|
83
|
+
f"Task {task_id} image status: {container_status} "
|
|
84
|
+
f"(exit={exit_code}) → {status}"
|
|
85
|
+
)
|
|
86
|
+
return status
|
manta_node/task_manager.py
CHANGED
|
@@ -3,11 +3,11 @@ from collections import deque
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from tempfile import TemporaryDirectory
|
|
6
|
-
from typing import List, Optional, Tuple
|
|
6
|
+
from typing import List, Optional, Set, Tuple
|
|
7
7
|
|
|
8
8
|
from manta_common.build.common.system import EnvId, PairId
|
|
9
9
|
from manta_common.build.common.tasks import MqttTask, TaskStatus, TaskUpdate
|
|
10
|
-
from manta_common.conversions import ID
|
|
10
|
+
from manta_common.conversions import ID, TupleID
|
|
11
11
|
from manta_common.errors import (
|
|
12
12
|
MantaError,
|
|
13
13
|
MantaGRPCError,
|
|
@@ -16,6 +16,7 @@ from manta_common.errors import (
|
|
|
16
16
|
)
|
|
17
17
|
from manta_common.traces import Tracer
|
|
18
18
|
from .domain.task_lifecycle import TaskLifecycleService
|
|
19
|
+
from .infrastructure.container.image_executor import ImageExecutor
|
|
19
20
|
from .infrastructure.container.manager import ContainerManager
|
|
20
21
|
from .infrastructure.grpc.client import NodeClient
|
|
21
22
|
from .tasks import TaskProgression, TaskProgressions
|
|
@@ -23,7 +24,7 @@ from .tasks import TaskProgression, TaskProgressions
|
|
|
23
24
|
__all__ = ["TaskManager"]
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
class TaskManager(ContainerManager):
|
|
27
|
+
class TaskManager(ContainerManager, ImageExecutor):
|
|
27
28
|
"""
|
|
28
29
|
The Task Manager is responsible to distribute tasks depending on device resources.
|
|
29
30
|
It is composed with a Queue of Tasks.
|
|
@@ -54,6 +55,7 @@ class TaskManager(ContainerManager):
|
|
|
54
55
|
# Initialize the task queue
|
|
55
56
|
self.task_queue = deque()
|
|
56
57
|
self.future_task = None
|
|
58
|
+
self.image_task_ids: Set[Tuple[TupleID, TupleID]] = set()
|
|
57
59
|
|
|
58
60
|
ContainerManager.__init__(self)
|
|
59
61
|
|
|
@@ -155,6 +157,11 @@ class TaskManager(ContainerManager):
|
|
|
155
157
|
updated.append(task_progression)
|
|
156
158
|
return updated
|
|
157
159
|
|
|
160
|
+
async def get_task_status(self, swarm_id: ID, task_id: ID) -> TaskStatus:
|
|
161
|
+
if (swarm_id.tid, task_id.tid) in self.image_task_ids:
|
|
162
|
+
return await self.get_image_task_status(swarm_id, task_id)
|
|
163
|
+
return await ContainerManager.get_task_status(self, swarm_id, task_id)
|
|
164
|
+
|
|
158
165
|
async def from_pending_to_running(self, task_progression: TaskProgression) -> bool:
|
|
159
166
|
"""
|
|
160
167
|
Change a task progression from pending status (supposed) to running status
|
|
@@ -219,56 +226,69 @@ class TaskManager(ContainerManager):
|
|
|
219
226
|
)
|
|
220
227
|
self.tracer.debug(f"Creating task {task_id} for {swarm_id}")
|
|
221
228
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
229
|
+
if not task.payload:
|
|
230
|
+
# Image execution path: native ENTRYPOINT, no zipapp, no gRPC inside
|
|
231
|
+
module_file = self.module_folder / f"{swarm_id.oid}_{task_id.oid}.img"
|
|
232
|
+
start_time = await self.run_image(
|
|
233
|
+
swarm_id=swarm_id,
|
|
234
|
+
task_id=task_id,
|
|
235
|
+
image=task.image,
|
|
236
|
+
environment={
|
|
237
|
+
"TASK_ID": task_id.xid,
|
|
238
|
+
"SWARM_ID": swarm_id.xid,
|
|
239
|
+
},
|
|
240
|
+
)
|
|
241
|
+
self.image_task_ids.add((swarm_id.tid, task_id.tid))
|
|
242
|
+
else:
|
|
243
|
+
# Zipapp execution path (existing)
|
|
244
|
+
module_file = self.module_folder / f"{swarm_id.oid}_{task_id.oid}.pyz"
|
|
245
|
+
module_file.unlink(missing_ok=True)
|
|
246
|
+
module_file.touch(exist_ok=True)
|
|
247
|
+
module_file.write_bytes(task.payload)
|
|
248
|
+
|
|
249
|
+
container_file = "/usr/src/module.pyz"
|
|
250
|
+
|
|
251
|
+
network = None
|
|
252
|
+
if task.network != str(None):
|
|
253
|
+
if task.auth_key != str(None) or task.login_server != str(None):
|
|
254
|
+
self.tracer.debug(
|
|
255
|
+
f"Task {task_id} has auth_key {task.auth_key} and login_server {task.login_server}"
|
|
256
|
+
)
|
|
257
|
+
network = await self.ensure_tailscale_service(
|
|
258
|
+
swarm_id,
|
|
259
|
+
task.auth_key,
|
|
260
|
+
task.login_server,
|
|
261
|
+
task.alias,
|
|
262
|
+
self.module_folder,
|
|
263
|
+
)
|
|
264
|
+
else:
|
|
265
|
+
self.tracer.error(
|
|
266
|
+
f"Task {task_id} has network {task.network} but no auth_key or login_server"
|
|
267
|
+
)
|
|
268
|
+
raise MantaNodeError(
|
|
269
|
+
f"Task {task_id} has network {task.network} but no auth_key or login_server",
|
|
270
|
+
metadata=self.tracer.metadata,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
start_time = await self.run_container(
|
|
274
|
+
swarm_id=swarm_id,
|
|
275
|
+
task_id=task_id,
|
|
276
|
+
image=task.image,
|
|
277
|
+
command=f"python {container_file}",
|
|
278
|
+
volumes={
|
|
279
|
+
module_file.absolute(): {"bind": container_file, "mode": "ro"}
|
|
280
|
+
},
|
|
281
|
+
environment={
|
|
282
|
+
"TASK_ID": task_id.xid,
|
|
283
|
+
"SWARM_ID": swarm_id.xid,
|
|
284
|
+
"RPC_PORT": self.ls_port,
|
|
285
|
+
"RPC_HOST": "host.docker.internal",
|
|
286
|
+
},
|
|
287
|
+
gpu=task.gpu,
|
|
288
|
+
network=network,
|
|
289
|
+
alias=task.alias,
|
|
290
|
+
)
|
|
270
291
|
|
|
271
|
-
# Update the task information
|
|
272
292
|
self.tasks.append(
|
|
273
293
|
TaskProgression(
|
|
274
294
|
task_update=TaskUpdate(
|
|
@@ -511,9 +531,6 @@ class TaskManager(ContainerManager):
|
|
|
511
531
|
task_progression.swarm_id,
|
|
512
532
|
task_progression.task_id,
|
|
513
533
|
)
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
task_progression.task_id.tid,
|
|
518
|
-
)
|
|
519
|
-
)
|
|
534
|
+
env_id = (task_progression.swarm_id.tid, task_progression.task_id.tid)
|
|
535
|
+
self.tasks.pop(env_id)
|
|
536
|
+
self.image_task_ids.discard(env_id)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: manta_node
|
|
3
|
-
Version: 0.5b0.
|
|
3
|
+
Version: 0.5b0.dev83
|
|
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=8Yy8ShoKbMemvm3UMPt9xJnoZsxiCptqdWnhAKJnhl4,18498
|
|
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
|
|
@@ -16,12 +16,13 @@ manta_node/cli/commands/start.py,sha256=49kvB6p01nBTp0yqLX36RiXeEccGYTcl9YMcB2sI
|
|
|
16
16
|
manta_node/cli/commands/status.py,sha256=9YYUaelmQJWqh6FbVuy6y2y1lvjoPIkA2d18JU0PHls,6430
|
|
17
17
|
manta_node/cli/commands/stop.py,sha256=mcO8AMtaPkpNFEy6lVIe4fvNFJSEkheFdxkj1ySiYaI,7719
|
|
18
18
|
manta_node/domain/__init__.py,sha256=uyJXDf0QgfGCsXLMggqKCB5QV9oneYJ4iM-REaJGXDM,217
|
|
19
|
-
manta_node/domain/task_lifecycle.py,sha256=
|
|
19
|
+
manta_node/domain/task_lifecycle.py,sha256=bnmarFdgrRoGkDeDlDj6OSOoCT8-qg00OMrny30stNI,3248
|
|
20
20
|
manta_node/infrastructure/__init__.py,sha256=yM60jKhdaD18BOsLb-0aR9D2XxGvrTnV92oOT7Kor2w,297
|
|
21
21
|
manta_node/infrastructure/config/__init__.py,sha256=jbRzb1c0RohpF83WOwtDpe19dBXVfbZYF3SoOaESh6k,617
|
|
22
22
|
manta_node/infrastructure/config/node_config_manager.py,sha256=iAzMWPjs2AWTxwDLAdCK8GpMMw_14QOzX11hvQMnSvY,30414
|
|
23
|
-
manta_node/infrastructure/container/__init__.py,sha256=
|
|
23
|
+
manta_node/infrastructure/container/__init__.py,sha256=EO4SndUwmXrHczM4qkleUbxC7lTGz3c3Hd3iGrPg6D8,262
|
|
24
24
|
manta_node/infrastructure/container/docker_adapter.py,sha256=Sxprl0RpjT8epxTzYUJxSyHko3IBcus2ihneq4kRH7Q,6980
|
|
25
|
+
manta_node/infrastructure/container/image_executor.py,sha256=PfLCqzpn8HVsxpPEpZmoQYnFQj4W91jrZUjcntnyzoQ,2881
|
|
25
26
|
manta_node/infrastructure/container/manager.py,sha256=qpHwBOsDdiv10i5Qlx9PUn5UHr1oy8ejRGalTCi6AOQ,17375
|
|
26
27
|
manta_node/infrastructure/filesystem/__init__.py,sha256=nf6Fakp3wuFKjSAm_oihI6WE_yyUY6Uelr7A1GwSvXs,145
|
|
27
28
|
manta_node/infrastructure/filesystem/dataset_manager.py,sha256=lCcsLdDHM-rRfhndpD9fQ56rPKYcuel-p3qx8nUxffE,14894
|
|
@@ -34,9 +35,9 @@ manta_node/infrastructure/metrics/collector.py,sha256=nhFPAbjA04ye3UaGN95_zdpf9h
|
|
|
34
35
|
manta_node/infrastructure/metrics/metrics_collector.py,sha256=jhEocAPWS6xgbNvcBBiEB9cAN_9hEGssZJXoysQzWOk,10518
|
|
35
36
|
manta_node/infrastructure/mqtt/__init__.py,sha256=riXo1sXgO4YV6DHCmWRqK61BAmuGLdHiVdQFunAJadA,140
|
|
36
37
|
manta_node/infrastructure/mqtt/command_handler.py,sha256=bDH8k3Vkbbb7q3P68VamhMbtLJ4vSSivqZP3IfMBqIQ,11833
|
|
37
|
-
manta_node-0.5b0.
|
|
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.
|
|
38
|
+
manta_node-0.5b0.dev83.dist-info/licenses/LICENSE,sha256=P5HojupjKoAusNtHqWOvO-YFolwLFWQ7Uq0XVKK9nFQ,35291
|
|
39
|
+
manta_node-0.5b0.dev83.dist-info/METADATA,sha256=hcm1pKCE158stT99YW-PdsVoFDNWbbmKtsj2sdFIXu0,44653
|
|
40
|
+
manta_node-0.5b0.dev83.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
41
|
+
manta_node-0.5b0.dev83.dist-info/entry_points.txt,sha256=C_zcw6mdmztNxVUZGLZ83UH5TwcSuqb2C-rB4e2CQoc,56
|
|
42
|
+
manta_node-0.5b0.dev83.dist-info/top_level.txt,sha256=nbT5J5WETuXM9UPoQckl29uUOq8wMYdRb9QDx5kAFeo,11
|
|
43
|
+
manta_node-0.5b0.dev83.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|